From 3b86552d669b44c13f8dcf72e1147f79e9f680c6 Mon Sep 17 00:00:00 2001 From: Ali Hasani Date: Sat, 23 May 2020 15:25:15 +0430 Subject: [PATCH 01/30] refactor: remove duplicated code in std/node/module (#5778) --- std/node/module.ts | 98 +--------------------------------------------- std/node/url.ts | 2 +- 2 files changed, 2 insertions(+), 98 deletions(-) diff --git a/std/node/module.ts b/std/node/module.ts index e73eb4d5d7b9f9..4e0c55c1d84a6f 100644 --- a/std/node/module.ts +++ b/std/node/module.ts @@ -31,6 +31,7 @@ import * as nodeQueryString from "./querystring.ts"; import * as path from "../path/mod.ts"; import { assert } from "../testing/asserts.ts"; +import { pathToFileURL, fileURLToPath } from "./url.ts"; const CHAR_FORWARD_SLASH = "/".charCodeAt(0); const CHAR_BACKWARD_SLASH = "\\".charCodeAt(0); @@ -1162,103 +1163,6 @@ function stripBOM(content: string): string { return content; } -const forwardSlashRegEx = /\//g; -const CHAR_LOWERCASE_A = "a".charCodeAt(0); -const CHAR_LOWERCASE_Z = "z".charCodeAt(0); - -function getPathFromURLWin32(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 Error( - "Invalid file url path: must not include encoded \\ or / characters" - ); - } - } - } - pathname = pathname.replace(forwardSlashRegEx, "\\"); - pathname = decodeURIComponent(pathname); - // TODO: handle windows hostname case (needs bindings) - 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 Error("Invalid file URL path: must be absolute"); - } - return pathname.slice(1); -} - -function getPathFromURLPosix(url: URL): string { - if (url.hostname !== "") { - throw new Error("Invalid file URL host"); - } - 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 Error( - "Invalid file URL path: must not include encoded / characters" - ); - } - } - } - return decodeURIComponent(pathname); -} - -function fileURLToPath(path: string | URL): string { - if (typeof path === "string") { - path = new URL(path); - } - if (path.protocol !== "file:") { - throw new Error("Protocol has to be file://"); - } - return isWindows ? getPathFromURLWin32(path) : getPathFromURLPosix(path); -} - -const percentRegEx = /%/g; -const backslashRegEx = /\\/g; -const newlineRegEx = /\n/g; -const carriageReturnRegEx = /\r/g; -const tabRegEx = /\t/g; -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; -} - export const builtinModules = Module.builtinModules; export const createRequire = Module.createRequire; export default Module; diff --git a/std/node/url.ts b/std/node/url.ts index bbf0ff5d00ab95..b0034d02d2ba38 100644 --- a/std/node/url.ts +++ b/std/node/url.ts @@ -72,7 +72,7 @@ function getPathFromURLWin(url: URL): string { return `\\\\${hostname}${pathname}`; } else { // Otherwise, it's a local path that requires a drive letter - const letter = pathname.codePointAt(1) || 0x20; + const letter = pathname.codePointAt(1)! | 0x20; const sep = pathname[2]; if ( letter < CHAR_LOWERCASE_A || From 6feca0ef8bec69f4940c28a9ac0b6a0eeb6884ba Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Sat, 23 May 2020 15:37:12 +0200 Subject: [PATCH 02/30] Fix Deno.dir and Diagnostics being present at stable runtime (#5750) --- cli/js/deno.ts | 8 +------- cli/js/deno_unstable.ts | 6 ++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cli/js/deno.ts b/cli/js/deno.ts index 5843d5fbfce808..5bc69fc7b192e5 100644 --- a/cli/js/deno.ts +++ b/cli/js/deno.ts @@ -13,12 +13,6 @@ export { chmodSync, chmod } from "./ops/fs/chmod.ts"; export { chownSync, chown } from "./ops/fs/chown.ts"; export { customInspect, inspect } from "./web/console.ts"; export { copyFileSync, copyFile } from "./ops/fs/copy_file.ts"; -export { - Diagnostic, - DiagnosticCategory, - DiagnosticItem, - DiagnosticMessageChain, -} from "./diagnostics.ts"; export { chdir, cwd } from "./ops/fs/dir.ts"; export { errors } from "./errors.ts"; export { @@ -59,7 +53,7 @@ export { export { metrics, Metrics } from "./ops/runtime.ts"; export { mkdirSync, mkdir, MkdirOptions } from "./ops/fs/mkdir.ts"; export { connect, listen, Listener, Conn } from "./net.ts"; -export { dir, env, exit, execPath } from "./ops/os.ts"; +export { env, exit, execPath } from "./ops/os.ts"; export { run, RunOptions, Process, ProcessStatus } from "./process.ts"; export { DirEntry, readDirSync, readDir } from "./ops/fs/read_dir.ts"; export { readFileSync, readFile } from "./read_file.ts"; diff --git a/cli/js/deno_unstable.ts b/cli/js/deno_unstable.ts index 4d964f3bd4ab28..192137e1f63441 100644 --- a/cli/js/deno_unstable.ts +++ b/cli/js/deno_unstable.ts @@ -23,3 +23,9 @@ export { PermissionStatus, Permissions, } from "./permissions.ts"; +export { + Diagnostic, + DiagnosticCategory, + DiagnosticItem, + DiagnosticMessageChain, +} from "./diagnostics.ts"; From 94f1de5f805638b7bc06ac36b7fb8adb568c7a25 Mon Sep 17 00:00:00 2001 From: guzhongren Date: Sat, 23 May 2020 22:16:57 +0800 Subject: [PATCH 03/30] Fix example (#5775) --- cli/js/lib.deno.ns.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/js/lib.deno.ns.d.ts b/cli/js/lib.deno.ns.d.ts index d2a18c4141f9bf..2415214a505d20 100644 --- a/cli/js/lib.deno.ns.d.ts +++ b/cli/js/lib.deno.ns.d.ts @@ -62,7 +62,7 @@ declare namespace Deno { * * Deno.test({ * name: "example ignored test", - * ignore: Deno.build.os === "windows" + * ignore: Deno.build.os === "windows", * fn(): void { * // This test is ignored only on Windows machines * }, @@ -73,7 +73,7 @@ declare namespace Deno { * async fn() { * const decoder = new TextDecoder("utf-8"); * const data = await Deno.readFile("hello_world.txt"); - * assertEquals(decoder.decode(data), "Hello world") + * assertEquals(decoder.decode(data), "Hello world"); * } * }); * ``` @@ -94,7 +94,7 @@ declare namespace Deno { * Deno.test("My async test description", async ():Promise => { * const decoder = new TextDecoder("utf-8"); * const data = await Deno.readFile("hello_world.txt"); - * assertEquals(decoder.decode(data), "Hello world") + * assertEquals(decoder.decode(data), "Hello world"); * }); * ``` * */ @@ -1828,7 +1828,7 @@ declare namespace Deno { * ```ts * const obj = {}; * obj.propA = 10; - * obj.propB = "hello" + * obj.propB = "hello"; * const objAsString = Deno.inspect(obj); // { propA: 10, propB: "hello" } * console.log(obj); // prints same value as objAsString, e.g. { propA: 10, propB: "hello" } * ``` From c3c16f025ca6f30f3faaa70481a125494ddcea87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Sat, 23 May 2020 19:04:29 +0200 Subject: [PATCH 04/30] fix: TSX analysis in module graph loader (#5785) --- cli/module_graph.rs | 11 ++++++++--- cli/swc_util.rs | 1 + cli/tests/Component.tsx | 1 + cli/tests/integration_tests.rs | 5 +++++ cli/tests/tsx_imports.ts | 1 + cli/tests/tsx_imports.ts.out | 2 ++ 6 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 cli/tests/Component.tsx create mode 100644 cli/tests/tsx_imports.ts create mode 100644 cli/tests/tsx_imports.ts.out diff --git a/cli/module_graph.rs b/cli/module_graph.rs index 8e423265cbd941..3a59a537d7f154 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -48,6 +48,13 @@ where } } +const SUPPORTED_MEDIA_TYPES: [MediaType; 4] = [ + MediaType::JavaScript, + MediaType::TypeScript, + MediaType::JSX, + MediaType::TSX, +]; + #[derive(Debug, Serialize)] pub struct ModuleGraph(HashMap); @@ -384,9 +391,7 @@ impl ModuleGraphLoader { let module_specifier = ModuleSpecifier::from(source_file.url.clone()); let source_code = String::from_utf8(source_file.source_code)?; - if source_file.media_type == MediaType::JavaScript - || source_file.media_type == MediaType::TypeScript - { + if SUPPORTED_MEDIA_TYPES.contains(&source_file.media_type) { if let Some(types_specifier) = source_file.types_header { let type_header = ReferenceDescriptor { specifier: types_specifier.to_string(), diff --git a/cli/swc_util.rs b/cli/swc_util.rs index d3b2a9e4af6c05..465fb9769e763c 100644 --- a/cli/swc_util.rs +++ b/cli/swc_util.rs @@ -147,6 +147,7 @@ impl AstParser { let mut ts_config = TsConfig::default(); ts_config.dynamic_import = true; ts_config.decorators = true; + ts_config.tsx = true; let syntax = Syntax::Typescript(ts_config); let lexer = Lexer::new( diff --git a/cli/tests/Component.tsx b/cli/tests/Component.tsx new file mode 100644 index 00000000000000..34208329bdf254 --- /dev/null +++ b/cli/tests/Component.tsx @@ -0,0 +1 @@ +import "./046_jsx_test.tsx"; \ No newline at end of file diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 457a345dd3fb12..b3f792b4e9bf3b 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -1723,6 +1723,11 @@ itest!(disallow_http_from_https_ts { exit_code: 1, }); +itest!(tsx_imports { + args: "run --reload tsx_imports.ts", + output: "tsx_imports.ts.out", +}); + itest!(fix_js_import_js { args: "run --quiet --reload fix_js_import_js.ts", output: "fix_js_import_js.ts.out", diff --git a/cli/tests/tsx_imports.ts b/cli/tests/tsx_imports.ts new file mode 100644 index 00000000000000..44ba10b7adc3c3 --- /dev/null +++ b/cli/tests/tsx_imports.ts @@ -0,0 +1 @@ +import "./Component.tsx"; diff --git a/cli/tests/tsx_imports.ts.out b/cli/tests/tsx_imports.ts.out new file mode 100644 index 00000000000000..4ea4308b1b8113 --- /dev/null +++ b/cli/tests/tsx_imports.ts.out @@ -0,0 +1,2 @@ +Compile [WILDCARD]tsx_imports.ts +{ factory: [Function: View], props: null, children: [] } From 2bbe475dbbd46c505cdb2a5e511caadbd63dd1fd Mon Sep 17 00:00:00 2001 From: Umar Bolatov Date: Sun, 24 May 2020 02:24:14 -0700 Subject: [PATCH 05/30] docs: update permissions example (#5809) --- docs/examples/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/permissions.md b/docs/examples/permissions.md index 2237d26df6b6bc..78dfcc59dbf56a 100644 --- a/docs/examples/permissions.md +++ b/docs/examples/permissions.md @@ -13,7 +13,7 @@ if (status.state !== "granted") { throw new Error("need write permission"); } -const log = await Deno.open("request.log", "a+"); +const log = await Deno.open("request.log", { write: true, append: true }); // revoke some permissions await Deno.permissions.revoke({ name: "read" }); From f6e31603563bad38c663494ddec6a363989b5786 Mon Sep 17 00:00:00 2001 From: skdltmxn Date: Sun, 24 May 2020 22:10:01 +0900 Subject: [PATCH 06/30] feat(std/encoding): add base64 (#5811) --- std/encoding/base64.ts | 41 ++++++++++++++++++++++++++++++++ std/encoding/base64_test.ts | 47 +++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 std/encoding/base64.ts create mode 100644 std/encoding/base64_test.ts diff --git a/std/encoding/base64.ts b/std/encoding/base64.ts new file mode 100644 index 00000000000000..2f74c8df0d564b --- /dev/null +++ b/std/encoding/base64.ts @@ -0,0 +1,41 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +/** + * Converts given data with base64 encoding + * @param data input to encode + */ +export function encode(data: string | ArrayBuffer): string { + if (typeof data === "string") { + return window.btoa(data); + } else { + const d = new Uint8Array(data); + let dataString = ""; + for (let i = 0; i < d.length; ++i) { + dataString += String.fromCharCode(d[i]); + } + + return window.btoa(dataString); + } +} + +/** + * Converts given base64 encoded data back to original + * @param data input to decode + */ +export function decode(data: string): ArrayBuffer { + const binaryString = decodeString(data); + const binary = new Uint8Array(binaryString.length); + for (let i = 0; i < binary.length; ++i) { + binary[i] = binaryString.charCodeAt(i); + } + + return binary.buffer; +} + +/** + * Decodes data assuming the output is in string type + * @param data input to decode + */ +export function decodeString(data: string): string { + return window.atob(data); +} diff --git a/std/encoding/base64_test.ts b/std/encoding/base64_test.ts new file mode 100644 index 00000000000000..bd559140ada323 --- /dev/null +++ b/std/encoding/base64_test.ts @@ -0,0 +1,47 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +const { test } = Deno; +import { assertEquals } from "../testing/asserts.ts"; +import { encode, decode, decodeString } from "./base64.ts"; + +const testsetString = [ + ["", ""], + ["f", "Zg=="], + ["fo", "Zm8="], + ["foo", "Zm9v"], + ["foob", "Zm9vYg=="], + ["fooba", "Zm9vYmE="], + ["foobar", "Zm9vYmFy"], +]; + +const testsetBinary = [ + [new TextEncoder().encode("\x00"), "AA=="], + [new TextEncoder().encode("\x00\x00"), "AAA="], + [new TextEncoder().encode("\x00\x00\x00"), "AAAA"], + [new TextEncoder().encode("\x00\x00\x00\x00"), "AAAAAA=="], +]; + +test("[encoding/base64] testBase64EncodeString", () => { + for (const [input, output] of testsetString) { + assertEquals(encode(input), output); + } +}); + +test("[encoding/base64] testBase64DecodeString", () => { + for (const [input, output] of testsetString) { + assertEquals(decodeString(output), input); + } +}); + +test("[encoding/base64] testBase64EncodeBinary", () => { + for (const [input, output] of testsetBinary) { + assertEquals(encode(input), output); + } +}); + +test("[encoding/base64] testBase64DecodeBinary", () => { + for (const [input, output] of testsetBinary) { + const outputBinary = new Uint8Array(decode(output as string)); + assertEquals(outputBinary, input as Uint8Array); + } +}); From b7f0b073bb7585ea932a5783ff4ea88843a46b1b Mon Sep 17 00:00:00 2001 From: Valentin Anger Date: Sun, 24 May 2020 15:43:40 +0200 Subject: [PATCH 07/30] Add unstable checks for unix transport (#5818) Also remove the unix example from the stable documentation to stay in line with the `Deno.listen` one --- cli/js/lib.deno.ns.d.ts | 3 +-- cli/js/lib.deno.unstable.d.ts | 2 +- cli/ops/net.rs | 4 ++++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cli/js/lib.deno.ns.d.ts b/cli/js/lib.deno.ns.d.ts index 2415214a505d20..fd0c0e6d73f323 100644 --- a/cli/js/lib.deno.ns.d.ts +++ b/cli/js/lib.deno.ns.d.ts @@ -1612,10 +1612,9 @@ declare namespace Deno { * const conn2 = await Deno.connect({ hostname: "192.0.2.1", port: 80 }); * const conn3 = await Deno.connect({ hostname: "[2001:db8::1]", port: 80 }); * const conn4 = await Deno.connect({ hostname: "golang.org", port: 80, transport: "tcp" }); - * const conn5 = await Deno.connect({ path: "/foo/bar.sock", transport: "unix" }); * ``` * - * Requires `allow-net` permission for "tcp" and `allow-read` for unix. */ + * Requires `allow-net` permission for "tcp". */ export function connect(options: ConnectOptions): Promise; export interface ConnectTlsOptions { diff --git a/cli/js/lib.deno.unstable.d.ts b/cli/js/lib.deno.unstable.d.ts index 9bb4cfb90bb74d..86bdd7b126c304 100644 --- a/cli/js/lib.deno.unstable.d.ts +++ b/cli/js/lib.deno.unstable.d.ts @@ -1067,7 +1067,7 @@ declare namespace Deno { * const conn5 = await Deno.connect({ path: "/foo/bar.sock", transport: "unix" }); * ``` * - * Requires `allow-net` permission for "tcp" and `allow-read` for unix. */ + * Requires `allow-net` permission for "tcp" and `allow-read` for "unix". */ export function connect( options: ConnectOptions | UnixConnectOptions ): Promise; diff --git a/cli/ops/net.rs b/cli/ops/net.rs index 59707e291ff287..ae5bcb9bba568e 100644 --- a/cli/ops/net.rs +++ b/cli/ops/net.rs @@ -302,6 +302,7 @@ fn op_connect( transport_args: ArgsEnum::Unix(args), } if transport == "unix" => { let address_path = net_unix::Path::new(&args.path); + state.check_unstable("Deno.connect"); state.check_read(&address_path)?; let op = async move { let path = args.path; @@ -524,6 +525,9 @@ fn op_listen( transport, transport_args: ArgsEnum::Unix(args), } if transport == "unix" || transport == "unixpacket" => { + if transport == "unix" { + state.check_unstable("Deno.listen"); + } if transport == "unixpacket" { state.check_unstable("Deno.listenDatagram"); } From 4ca0d6e2d37ce2be029269be498c76922e30944b Mon Sep 17 00:00:00 2001 From: Andrew Mitchell <32021055+mitch292@users.noreply.github.com> Date: Sun, 24 May 2020 16:04:57 +0000 Subject: [PATCH 08/30] Re-enable several fetch tests (#5803) --- cli/tests/unit/fetch_test.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts index 37fca21123830c..458c34ae23dc1e 100644 --- a/cli/tests/unit/fetch_test.ts +++ b/cli/tests/unit/fetch_test.ts @@ -367,8 +367,6 @@ function bufferServer(addr: string): Deno.Buffer { unitTest( { - // FIXME(bartlomieju) - ignore: true, perms: { net: true }, }, async function fetchRequest(): Promise { @@ -381,6 +379,7 @@ unitTest( ["Foo", "Bar"], ], }); + await response.arrayBuffer(); assertEquals(response.status, 404); assertEquals(response.headers.get("Content-Length"), "2"); @@ -389,6 +388,9 @@ unitTest( "POST /blah HTTP/1.1\r\n", "hello: World\r\n", "foo: Bar\r\n", + "accept: */*\r\n", + `user-agent: Deno/${Deno.version.deno}\r\n`, + "accept-encoding: gzip, br\r\n", `host: ${addr}\r\n\r\n`, ].join(""); assertEquals(actual, expected); @@ -397,8 +399,6 @@ unitTest( unitTest( { - // FIXME(bartlomieju) - ignore: true, perms: { net: true }, }, async function fetchPostBodyString(): Promise { @@ -413,6 +413,7 @@ unitTest( ], body, }); + await response.arrayBuffer(); assertEquals(response.status, 404); assertEquals(response.headers.get("Content-Length"), "2"); @@ -421,6 +422,10 @@ unitTest( "POST /blah HTTP/1.1\r\n", "hello: World\r\n", "foo: Bar\r\n", + "content-type: text/plain;charset=UTF-8\r\n", + "accept: */*\r\n", + `user-agent: Deno/${Deno.version.deno}\r\n`, + "accept-encoding: gzip, br\r\n", `host: ${addr}\r\n`, `content-length: ${body.length}\r\n\r\n`, body, @@ -431,8 +436,6 @@ unitTest( unitTest( { - // FIXME(bartlomieju) - ignore: true, perms: { net: true }, }, async function fetchPostBodyTypedArray(): Promise { @@ -448,6 +451,7 @@ unitTest( ], body, }); + await response.arrayBuffer(); assertEquals(response.status, 404); assertEquals(response.headers.get("Content-Length"), "2"); @@ -456,6 +460,9 @@ unitTest( "POST /blah HTTP/1.1\r\n", "hello: World\r\n", "foo: Bar\r\n", + "accept: */*\r\n", + `user-agent: Deno/${Deno.version.deno}\r\n`, + "accept-encoding: gzip, br\r\n", `host: ${addr}\r\n`, `content-length: ${body.byteLength}\r\n\r\n`, bodyStr, From e934df5f7dd7ebc52e8c74033d478c88fa638224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Sun, 24 May 2020 19:20:40 +0200 Subject: [PATCH 09/30] fix: create HTTP cache lazily (#5795) --- cli/deno_dir.rs | 2 +- cli/disk_cache.rs | 19 +++++++++++-------- cli/global_state.rs | 1 - cli/http_cache.rs | 29 +++++++++++++++++++++++------ 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/cli/deno_dir.rs b/cli/deno_dir.rs index 3224fbf4674978..1447682ec63eac 100644 --- a/cli/deno_dir.rs +++ b/cli/deno_dir.rs @@ -40,7 +40,7 @@ impl DenoDir { root, gen_cache: DiskCache::new(&gen_path), }; - deno_dir.gen_cache.ensure_location()?; + deno_dir.gen_cache.ensure_dir_exists(&gen_path)?; Ok(deno_dir) } diff --git a/cli/disk_cache.rs b/cli/disk_cache.rs index 828ca90ca615a9..58744acd8fea8a 100644 --- a/cli/disk_cache.rs +++ b/cli/disk_cache.rs @@ -31,14 +31,14 @@ impl DiskCache { } /// Ensures the location of the cache. - pub fn ensure_location(&self) -> io::Result<()> { - if self.location.is_dir() { + pub fn ensure_dir_exists(&self, path: &Path) -> io::Result<()> { + if path.is_dir() { return Ok(()); } - fs::create_dir_all(&self.location).map_err(|e| { + fs::create_dir_all(&path).map_err(|e| { io::Error::new(e.kind(), format!( "Could not create TypeScript compiler cache location: {:?}\nCheck the permission of the directory.", - self.location + path )) }) } @@ -129,8 +129,7 @@ impl DiskCache { pub fn set(&self, filename: &Path, data: &[u8]) -> std::io::Result<()> { let path = self.location.join(filename); match path.parent() { - Some(ref parent) => fs::create_dir_all(parent) - .map_err(|e| with_io_context(&e, format!("{:#?}", &path))), + Some(ref parent) => self.ensure_dir_exists(parent), None => Ok(()), }?; deno_fs::write_file(&path, data, 0o666) @@ -154,7 +153,9 @@ mod tests { let mut cache_path = cache_location.path().to_owned(); cache_path.push("foo"); let cache = DiskCache::new(&cache_path); - cache.ensure_location().expect("Testing expect:"); + cache + .ensure_dir_exists(&cache.location) + .expect("Testing expect:"); assert!(cache_path.is_dir()); } @@ -166,7 +167,9 @@ mod tests { cache_location.push("foo"); assert_eq!(cache_location.is_dir(), false); let cache = DiskCache::new(&cache_location); - cache.ensure_location().expect("Testing expect:"); + cache + .ensure_dir_exists(&cache.location) + .expect("Testing expect:"); assert_eq!(cache_location.is_dir(), true); } diff --git a/cli/global_state.rs b/cli/global_state.rs index 90961ed6271a8c..8bd01f438fa9aa 100644 --- a/cli/global_state.rs +++ b/cli/global_state.rs @@ -51,7 +51,6 @@ impl GlobalState { let dir = deno_dir::DenoDir::new(custom_root)?; let deps_cache_location = dir.root.join("deps"); let http_cache = http_cache::HttpCache::new(&deps_cache_location); - http_cache.ensure_location()?; let file_fetcher = SourceFileFetcher::new( http_cache, diff --git a/cli/http_cache.rs b/cli/http_cache.rs index 2a9882376a0049..23b482840d68bb 100644 --- a/cli/http_cache.rs +++ b/cli/http_cache.rs @@ -113,16 +113,16 @@ impl HttpCache { } /// Ensures the location of the cache. - pub fn ensure_location(&self) -> io::Result<()> { - if self.location.is_dir() { + fn ensure_dir_exists(&self, path: &Path) -> io::Result<()> { + if path.is_dir() { return Ok(()); } - fs::create_dir_all(&self.location).map_err(|e| { + fs::create_dir_all(&path).map_err(|e| { io::Error::new( e.kind(), format!( "Could not create remote modules cache location: {:?}\nCheck the permission of the directory.", - self.location + path ), ) }) @@ -163,7 +163,7 @@ impl HttpCache { let parent_filename = cache_filename .parent() .expect("Cache filename should have a parent dir"); - fs::create_dir_all(parent_filename)?; + self.ensure_dir_exists(parent_filename)?; // Cache content deno_fs::write_file(&cache_filename, content, 0o666)?; @@ -187,8 +187,25 @@ mod tests { let dir = TempDir::new().unwrap(); let mut cache_path = dir.path().to_owned(); cache_path.push("foobar"); + // HttpCache should be created lazily on first use: + // when zipping up a local project with no external dependencies + // "$DENO_DIR/deps" is empty. When unzipping such project + // "$DENO_DIR/deps" might not get restored and in situation + // when directory is owned by root we might not be able + // to create that directory. However if it's not needed it + // doesn't make sense to return error in such specific scenarios. + // For more details check issue: + // https://github.com/denoland/deno/issues/5688 let cache = HttpCache::new(&cache_path); - assert!(cache.ensure_location().is_ok()); + assert!(!cache.location.exists()); + cache + .set( + &Url::parse("http://example.com/foo/bar.js").unwrap(), + HeadersMap::new(), + b"hello world", + ) + .expect("Failed to add to cache"); + assert!(cache.ensure_dir_exists(&cache.location).is_ok()); assert!(cache_path.is_dir()); } From ee0b5bb89ec33acc5dafb876de1f9fda5bcfa236 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Mon, 25 May 2020 02:08:59 +0200 Subject: [PATCH 10/30] test: add utility function for assigning unique port to inspector (#5822) --- cli/tests/integration_tests.rs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index b3f792b4e9bf3b..983011d64ab80a 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -2207,6 +2207,13 @@ fn test_permissions_net_listen_allow_localhost() { assert!(!err.contains(util::PERMISSION_DENIED_PATTERN)); } +fn inspect_flag_with_unique_port(flag_prefix: &str) -> String { + use std::sync::atomic::{AtomicU16, Ordering}; + static PORT: AtomicU16 = AtomicU16::new(9229); + let port = PORT.fetch_add(1, Ordering::Relaxed); + format!("{}=127.0.0.1:{}", flag_prefix, port) +} + fn extract_ws_url_from_stderr( stderr: &mut std::process::ChildStderr, ) -> url::Url { @@ -2226,9 +2233,7 @@ async fn inspector_connect() { let script = util::tests_path().join("inspector1.js"); let mut child = util::deno_cmd() .arg("run") - // Warning: each inspector test should be on its own port to avoid - // conflicting with another inspector test. - .arg("--inspect=127.0.0.1:9228") + .arg(inspect_flag_with_unique_port("--inspect")) .arg(script) .stderr(std::process::Stdio::piped()) .spawn() @@ -2256,9 +2261,7 @@ async fn inspector_break_on_first_line() { let script = util::tests_path().join("inspector2.js"); let mut child = util::deno_cmd() .arg("run") - // Warning: each inspector test should be on its own port to avoid - // conflicting with another inspector test. - .arg("--inspect-brk=127.0.0.1:9229") + .arg(inspect_flag_with_unique_port("--inspect-brk")) .arg(script) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped()) @@ -2322,9 +2325,7 @@ async fn inspector_pause() { let script = util::tests_path().join("inspector1.js"); let mut child = util::deno_cmd() .arg("run") - // Warning: each inspector test should be on its own port to avoid - // conflicting with another inspector test. - .arg("--inspect=127.0.0.1:9230") + .arg(inspect_flag_with_unique_port("--inspect")) .arg(script) .stderr(std::process::Stdio::piped()) .spawn() @@ -2376,9 +2377,11 @@ async fn inspector_pause() { #[tokio::test] async fn inspector_port_collision() { let script = util::tests_path().join("inspector1.js"); + let inspect_flag = inspect_flag_with_unique_port("--inspect"); + let mut child1 = util::deno_cmd() .arg("run") - .arg("--inspect=127.0.0.1:9231") + .arg(&inspect_flag) .arg(script.clone()) .stderr(std::process::Stdio::piped()) .spawn() @@ -2388,7 +2391,7 @@ async fn inspector_port_collision() { let mut child2 = util::deno_cmd() .arg("run") - .arg("--inspect=127.0.0.1:9231") + .arg(&inspect_flag) .arg(script) .stderr(std::process::Stdio::piped()) .spawn() @@ -2414,9 +2417,7 @@ async fn inspector_does_not_hang() { let script = util::tests_path().join("inspector3.js"); let mut child = util::deno_cmd() .arg("run") - // Warning: each inspector test should be on its own port to avoid - // conflicting with another inspector test. - .arg("--inspect-brk=127.0.0.1:9232") + .arg(inspect_flag_with_unique_port("--inspect-brk")) .env("NO_COLOR", "1") .arg(script) .stdout(std::process::Stdio::piped()) @@ -2498,9 +2499,7 @@ async fn inspector_without_brk_runs_code() { let script = util::tests_path().join("inspector4.js"); let mut child = util::deno_cmd() .arg("run") - // Warning: each inspector test should be on its own port to avoid - // conflicting with another inspector test. - .arg("--inspect=127.0.0.1:9233") + .arg(inspect_flag_with_unique_port("--inspect")) .arg(script) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped()) From 131f2a5f0cdcdbebe3e17c447869f92c99a7c051 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Sun, 24 May 2020 22:28:19 +0200 Subject: [PATCH 11/30] fix: BorrowMutError when evaluating expression in inspector console (#5822) Note that this does not fix the 'Uncaught ReferenceError' issue that happens when 'eager evaluation' is enabled in the inspector. Fixes: #5807 --- cli/state.rs | 36 --------- cli/tests/integration_tests.rs | 137 +++++++++++++++++++++++++++++---- cli/worker.rs | 44 +++++++---- 3 files changed, 147 insertions(+), 70 deletions(-) diff --git a/cli/state.rs b/cli/state.rs index 46fbdfd0b27191..baac89216ff87c 100644 --- a/cli/state.rs +++ b/cli/state.rs @@ -3,7 +3,6 @@ use crate::file_fetcher::SourceFileFetcher; use crate::global_state::GlobalState; use crate::global_timer::GlobalTimer; use crate::import_map::ImportMap; -use crate::inspector::DenoInspector; use crate::metrics::Metrics; use crate::op_error::OpError; use crate::ops::JsonOp; @@ -12,7 +11,6 @@ use crate::permissions::Permissions; use crate::tsc::TargetLib; use crate::web_worker::WebWorkerHandle; use deno_core::Buf; -use deno_core::CoreIsolate; use deno_core::ErrBox; use deno_core::ModuleLoadId; use deno_core::ModuleLoader; @@ -61,7 +59,6 @@ pub struct StateInner { pub target_lib: TargetLib, pub is_main: bool, pub is_internal: bool, - pub inspector: Option>, } impl State { @@ -420,7 +417,6 @@ impl State { target_lib: TargetLib::Main, is_main: true, is_internal, - inspector: None, })); Ok(Self(state)) @@ -457,7 +453,6 @@ impl State { target_lib: TargetLib::Worker, is_main: false, is_internal: false, - inspector: None, })); Ok(Self(state)) @@ -526,37 +521,6 @@ impl State { } } - pub fn maybe_init_inspector(&self, isolate: &mut CoreIsolate) { - let mut state = self.borrow_mut(); - - if state.is_internal { - return; - }; - - let inspector_host = { - let global_state = &state.global_state; - match global_state - .flags - .inspect - .or(global_state.flags.inspect_brk) - { - Some(host) => host, - None => return, - } - }; - - let inspector = DenoInspector::new(isolate, inspector_host); - state.inspector.replace(inspector); - } - - #[inline] - pub fn should_inspector_break_on_first_statement(&self) -> bool { - let state = self.borrow(); - state.inspector.is_some() - && state.is_main - && state.global_state.flags.inspect_brk.is_some() - } - #[cfg(test)] pub fn mock(main_module: &str) -> State { let module_specifier = ModuleSpecifier::resolve_url_or_path(main_module) diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 983011d64ab80a..a9d3ebd6c194a5 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -2215,11 +2215,9 @@ fn inspect_flag_with_unique_port(flag_prefix: &str) -> String { } fn extract_ws_url_from_stderr( - stderr: &mut std::process::ChildStderr, + stderr_lines: &mut impl std::iter::Iterator, ) -> url::Url { - let mut stderr = std::io::BufReader::new(stderr); - let mut stderr_first_line = String::from(""); - let _ = stderr.read_line(&mut stderr_first_line).unwrap(); + let stderr_first_line = stderr_lines.next().unwrap(); assert!(stderr_first_line.starts_with("Debugger listening on ")); let v: Vec<_> = stderr_first_line.match_indices("ws:").collect(); assert_eq!(v.len(), 1); @@ -2238,8 +2236,12 @@ async fn inspector_connect() { .stderr(std::process::Stdio::piped()) .spawn() .unwrap(); - let ws_url = extract_ws_url_from_stderr(child.stderr.as_mut().unwrap()); - println!("ws_url {}", ws_url); + + let stderr = child.stderr.as_mut().unwrap(); + let mut stderr_lines = + std::io::BufReader::new(stderr).lines().map(|r| r.unwrap()); + let ws_url = extract_ws_url_from_stderr(&mut stderr_lines); + // We use tokio_tungstenite as a websocket client because warp (which is // a dependency of Deno) uses it. let (_socket, response) = tokio_tungstenite::connect_async(ws_url) @@ -2252,6 +2254,7 @@ async fn inspector_connect() { enum TestStep { StdOut(&'static str), + StdErr(&'static str), WsRecv(&'static str), WsSend(&'static str), } @@ -2269,7 +2272,10 @@ async fn inspector_break_on_first_line() { .unwrap(); let stderr = child.stderr.as_mut().unwrap(); - let ws_url = extract_ws_url_from_stderr(stderr); + let mut stderr_lines = + std::io::BufReader::new(stderr).lines().map(|r| r.unwrap()); + let ws_url = extract_ws_url_from_stderr(&mut stderr_lines); + let (socket, response) = tokio_tungstenite::connect_async(ws_url) .await .expect("Can't connect"); @@ -2313,6 +2319,7 @@ async fn inspector_break_on_first_line() { StdOut(s) => assert_eq!(&stdout_lines.next().unwrap(), s), WsRecv(s) => assert!(socket_rx.next().await.unwrap().starts_with(s)), WsSend(s) => socket_tx.send(s.into()).await.unwrap(), + _ => unreachable!(), } } @@ -2330,7 +2337,12 @@ async fn inspector_pause() { .stderr(std::process::Stdio::piped()) .spawn() .unwrap(); - let ws_url = extract_ws_url_from_stderr(child.stderr.as_mut().unwrap()); + + let stderr = child.stderr.as_mut().unwrap(); + let mut stderr_lines = + std::io::BufReader::new(stderr).lines().map(|r| r.unwrap()); + let ws_url = extract_ws_url_from_stderr(&mut stderr_lines); + // We use tokio_tungstenite as a websocket client because warp (which is // a dependency of Deno) uses it. let (mut socket, _) = tokio_tungstenite::connect_async(ws_url) @@ -2386,8 +2398,12 @@ async fn inspector_port_collision() { .stderr(std::process::Stdio::piped()) .spawn() .unwrap(); - let ws_url_1 = extract_ws_url_from_stderr(child1.stderr.as_mut().unwrap()); - println!("ws_url {}", ws_url_1); + + let stderr_1 = child1.stderr.as_mut().unwrap(); + let mut stderr_lines_1 = std::io::BufReader::new(stderr_1) + .lines() + .map(|r| r.unwrap()); + let _ = extract_ws_url_from_stderr(&mut stderr_lines_1); let mut child2 = util::deno_cmd() .arg("run") @@ -2426,7 +2442,10 @@ async fn inspector_does_not_hang() { .unwrap(); let stderr = child.stderr.as_mut().unwrap(); - let ws_url = extract_ws_url_from_stderr(stderr); + let mut stderr_lines = + std::io::BufReader::new(stderr).lines().map(|r| r.unwrap()); + let ws_url = extract_ws_url_from_stderr(&mut stderr_lines); + let (socket, response) = tokio_tungstenite::connect_async(ws_url) .await .expect("Can't connect"); @@ -2505,16 +2524,100 @@ async fn inspector_without_brk_runs_code() { .stderr(std::process::Stdio::piped()) .spawn() .unwrap(); - extract_ws_url_from_stderr(child.stderr.as_mut().unwrap()); + + let stderr = child.stderr.as_mut().unwrap(); + let mut stderr_lines = + std::io::BufReader::new(stderr).lines().map(|r| r.unwrap()); + let _ = extract_ws_url_from_stderr(&mut stderr_lines); // Check that inspector actually runs code without waiting for inspector - // connection - let mut stdout = std::io::BufReader::new(child.stdout.as_mut().unwrap()); - let mut stdout_first_line = String::from(""); - let _ = stdout.read_line(&mut stdout_first_line).unwrap(); - assert_eq!(stdout_first_line, "hello\n"); + // connection. + let stdout = child.stdout.as_mut().unwrap(); + let mut stdout_lines = + std::io::BufReader::new(stdout).lines().map(|r| r.unwrap()); + let stdout_first_line = stdout_lines.next().unwrap(); + assert_eq!(stdout_first_line, "hello"); child.kill().unwrap(); + child.wait().unwrap(); +} + +#[tokio::test] +async fn inspector_runtime_evaluate_does_not_crash() { + let mut child = util::deno_cmd() + .arg("repl") + .arg(inspect_flag_with_unique_port("--inspect")) + .stdin(std::process::Stdio::piped()) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .spawn() + .unwrap(); + + let stderr = child.stderr.as_mut().unwrap(); + let mut stderr_lines = std::io::BufReader::new(stderr) + .lines() + .map(|r| r.unwrap()) + .filter(|s| s.as_str() != "Debugger session started."); + let ws_url = extract_ws_url_from_stderr(&mut stderr_lines); + + let (socket, response) = tokio_tungstenite::connect_async(ws_url) + .await + .expect("Can't connect"); + assert_eq!(response.status(), 101); // Switching protocols. + + let (mut socket_tx, socket_rx) = socket.split(); + let mut socket_rx = + socket_rx.map(|msg| msg.unwrap().to_string()).filter(|msg| { + let pass = !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#); + futures::future::ready(pass) + }); + + let stdin = child.stdin.take().unwrap(); + + let stdout = child.stdout.as_mut().unwrap(); + let mut stdout_lines = std::io::BufReader::new(stdout) + .lines() + .map(|r| r.unwrap()) + .filter(|s| !s.starts_with("Deno ")); + + use TestStep::*; + let test_steps = vec![ + WsSend(r#"{"id":1,"method":"Runtime.enable"}"#), + WsSend(r#"{"id":2,"method":"Debugger.enable"}"#), + WsRecv( + r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#, + ), + WsRecv(r#"{"id":1,"result":{}}"#), + WsRecv(r#"{"id":2,"result":{"debuggerId":"#), + WsSend(r#"{"id":3,"method":"Runtime.runIfWaitingForDebugger"}"#), + WsRecv(r#"{"id":3,"result":{}}"#), + StdOut("exit using ctrl+d or close()"), + WsSend( + r#"{"id":4,"method":"Runtime.compileScript","params":{"expression":"Deno.cwd()","sourceURL":"","persistScript":false,"executionContextId":1}}"#, + ), + WsRecv(r#"{"id":4,"result":{}}"#), + WsSend( + r#"{"id":5,"method":"Runtime.evaluate","params":{"expression":"Deno.cwd()","objectGroup":"console","includeCommandLineAPI":true,"silent":false,"contextId":1,"returnByValue":true,"generatePreview":true,"userGesture":true,"awaitPromise":false,"replMode":true}}"#, + ), + WsRecv(r#"{"id":5,"result":{"result":{"type":"string","value":""#), + WsSend( + r#"{"id":6,"method":"Runtime.evaluate","params":{"expression":"console.error('done');","objectGroup":"console","includeCommandLineAPI":true,"silent":false,"contextId":1,"returnByValue":true,"generatePreview":true,"userGesture":true,"awaitPromise":false,"replMode":true}}"#, + ), + WsRecv(r#"{"id":6,"result":{"result":{"type":"undefined"}}}"#), + StdErr("done"), + ]; + + for step in test_steps { + match step { + StdOut(s) => assert_eq!(&stdout_lines.next().unwrap(), s), + StdErr(s) => assert_eq!(&stderr_lines.next().unwrap(), s), + WsRecv(s) => assert!(socket_rx.next().await.unwrap().starts_with(s)), + WsSend(s) => socket_tx.send(s.into()).await.unwrap(), + } + } + + std::mem::drop(stdin); + child.wait().unwrap(); } #[test] diff --git a/cli/worker.rs b/cli/worker.rs index b66c1cb06b1548..66aa98e6021c9e 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -12,7 +12,6 @@ use futures::channel::mpsc; use futures::future::FutureExt; use futures::stream::StreamExt; use futures::task::AtomicWaker; -use std::cell::RefMut; use std::env; use std::future::Future; use std::ops::Deref; @@ -88,6 +87,7 @@ fn create_channels() -> (WorkerChannelsInternal, WorkerHandle) { pub struct Worker { pub name: String, pub isolate: Box, + pub inspector: Option>, pub state: State, pub waker: AtomicWaker, pub(crate) internal_channels: WorkerChannelsInternal, @@ -99,18 +99,30 @@ impl Worker { let loader = Rc::new(state.clone()); let mut isolate = deno_core::EsIsolate::new(loader, startup_data, false); - state.maybe_init_inspector(&mut isolate); + { + let global_state = state.borrow().global_state.clone(); + isolate.set_js_error_create_fn(move |core_js_error| { + JSError::create(core_js_error, &global_state.ts_compiler) + }); + } - let global_state = state.borrow().global_state.clone(); - isolate.set_js_error_create_fn(move |core_js_error| { - JSError::create(core_js_error, &global_state.ts_compiler) - }); + let inspector = { + let state = state.borrow(); + let global_state = &state.global_state; + global_state + .flags + .inspect + .or(global_state.flags.inspect_brk) + .filter(|_| !state.is_internal) + .map(|inspector_host| DenoInspector::new(&mut isolate, inspector_host)) + }; let (internal_channels, external_channels) = create_channels(); Self { name, isolate, + inspector, state, waker: AtomicWaker::new(), internal_channels, @@ -173,16 +185,14 @@ impl Worker { self.external_channels.clone() } - #[inline(always)] - fn inspector(&self) -> RefMut>> { - let state = self.state.borrow_mut(); - RefMut::map(state, |s| &mut s.inspector) - } - - fn wait_for_inspector_session(&self) { - if self.state.should_inspector_break_on_first_statement() { + fn wait_for_inspector_session(&mut self) { + let should_break_on_first_statement = self.inspector.is_some() && { + let state = self.state.borrow(); + state.is_main && state.global_state.flags.inspect_brk.is_some() + }; + if should_break_on_first_statement { self - .inspector() + .inspector .as_mut() .unwrap() .wait_for_session_and_break_on_next_statement() @@ -194,7 +204,7 @@ impl Drop for Worker { fn drop(&mut self) { // The Isolate object must outlive the Inspector object, but this is // currently not enforced by the type system. - self.inspector().take(); + self.inspector.take(); } } @@ -205,7 +215,7 @@ impl Future for Worker { let inner = self.get_mut(); // We always poll the inspector if it exists. - let _ = inner.inspector().as_mut().map(|i| i.poll_unpin(cx)); + let _ = inner.inspector.as_mut().map(|i| i.poll_unpin(cx)); inner.waker.register(cx.waker()); inner.isolate.poll_unpin(cx) } From 20bf04dc7e046e8191443e75be451ec81523a86a Mon Sep 17 00:00:00 2001 From: Marcos Casagrande Date: Mon, 25 May 2020 15:12:09 +0200 Subject: [PATCH 12/30] Move getHeaderValueParams & hasHeaderValueOf to util.ts (#5824) --- cli/js/web/body.ts | 18 +----------------- cli/js/web/fetch.ts | 18 +----------------- cli/js/web/util.ts | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/cli/js/web/body.ts b/cli/js/web/body.ts index a1cf2038ae6a6c..f65ed9cd0e049e 100644 --- a/cli/js/web/body.ts +++ b/cli/js/web/body.ts @@ -2,6 +2,7 @@ import * as blob from "./blob.ts"; import * as encoding from "./text_encoding.ts"; import * as domTypes from "./dom_types.d.ts"; import { ReadableStreamImpl } from "./streams/readable_stream.ts"; +import { getHeaderValueParams, hasHeaderValueOf } from "./util.ts"; // only namespace imports work for now, plucking out what we need const { TextEncoder, TextDecoder } = encoding; @@ -90,23 +91,6 @@ function bufferFromStream(stream: ReadableStreamReader): Promise { }); } -function getHeaderValueParams(value: string): Map { - const params = new Map(); - // Forced to do so for some Map constructor param mismatch - value - .split(";") - .slice(1) - .map((s): string[] => s.trim().split("=")) - .filter((arr): boolean => arr.length > 1) - .map(([k, v]): [string, string] => [k, v.replace(/^"([^"]*)"$/, "$1")]) - .forEach(([k, v]): Map => params.set(k, v)); - return params; -} - -function hasHeaderValueOf(s: string, value: string): boolean { - return new RegExp(`^${value}[\t\s]*;?`).test(s); -} - export const BodyUsedError = "Failed to execute 'clone' on 'Body': body is already used"; diff --git a/cli/js/web/fetch.ts b/cli/js/web/fetch.ts index 38ca03aca7be5a..3485a770acd5ce 100644 --- a/cli/js/web/fetch.ts +++ b/cli/js/web/fetch.ts @@ -10,23 +10,7 @@ import { close } from "../ops/resources.ts"; import { Buffer } from "../buffer.ts"; import { fetch as opFetch, FetchResponse } from "../ops/fetch.ts"; import { DomFileImpl } from "./dom_file.ts"; - -function getHeaderValueParams(value: string): Map { - const params = new Map(); - // Forced to do so for some Map constructor param mismatch - value - .split(";") - .slice(1) - .map((s): string[] => s.trim().split("=")) - .filter((arr): boolean => arr.length > 1) - .map(([k, v]): [string, string] => [k, v.replace(/^"([^"]*)"$/, "$1")]) - .forEach(([k, v]): Map => params.set(k, v)); - return params; -} - -function hasHeaderValueOf(s: string, value: string): boolean { - return new RegExp(`^${value}[\t\s]*;?`).test(s); -} +import { getHeaderValueParams, hasHeaderValueOf } from "./util.ts"; class Body implements domTypes.Body, ReadableStream, io.Reader, io.Closer { diff --git a/cli/js/web/util.ts b/cli/js/web/util.ts index 0ab367554a556d..53ff8ef2273e95 100644 --- a/cli/js/web/util.ts +++ b/cli/js/web/util.ts @@ -189,3 +189,22 @@ export function defineEnumerableProps( Reflect.defineProperty(Ctor.prototype, prop, { enumerable: true }); } } + +// @internal +export function getHeaderValueParams(value: string): Map { + const params = new Map(); + // Forced to do so for some Map constructor param mismatch + value + .split(";") + .slice(1) + .map((s): string[] => s.trim().split("=")) + .filter((arr): boolean => arr.length > 1) + .map(([k, v]): [string, string] => [k, v.replace(/^"([^"]*)"$/, "$1")]) + .forEach(([k, v]): Map => params.set(k, v)); + return params; +} + +// @internal +export function hasHeaderValueOf(s: string, value: string): boolean { + return new RegExp(`^${value}[\t\s]*;?`).test(s); +} From fbbb9f1c36db526edc136fa2ecc4e6aba022099b Mon Sep 17 00:00:00 2001 From: Martin Suchanek Date: Mon, 25 May 2020 06:12:45 -0700 Subject: [PATCH 13/30] Add missing async delay import to code sample (#5837) --- docs/testing.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/testing.md b/docs/testing.md index 5448f6d5823775..ba484c9720a3d6 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -35,6 +35,8 @@ You can also test asynchronous code by passing a test function that returns a promise. For this you can use the `async` keyword when defining a function: ```ts +import { delay } from "https://deno.land/std/async/delay.ts"; + Deno.test("async hello world", async () => { const x = 1 + 2; From c9f0e34e294241541ba59c3a7eb52f42df7ff993 Mon Sep 17 00:00:00 2001 From: Marcos Casagrande Date: Mon, 25 May 2020 15:14:01 +0200 Subject: [PATCH 14/30] Improve bufferFromStream (#5826) --- cli/js/web/body.ts | 49 ++++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/cli/js/web/body.ts b/cli/js/web/body.ts index f65ed9cd0e049e..f0d88be4b42e5f 100644 --- a/cli/js/web/body.ts +++ b/cli/js/web/body.ts @@ -59,36 +59,29 @@ function concatenate(...arrays: Uint8Array[]): ArrayBuffer { return result.buffer as ArrayBuffer; } -function bufferFromStream(stream: ReadableStreamReader): Promise { - return new Promise((resolve, reject): void => { - const parts: Uint8Array[] = []; - const encoder = new TextEncoder(); - // recurse - (function pump(): void { - stream - .read() - .then(({ done, value }): void => { - if (done) { - return resolve(concatenate(...parts)); - } +async function bufferFromStream( + stream: ReadableStreamReader +): Promise { + const parts: Uint8Array[] = []; + const encoder = new TextEncoder(); - if (typeof value === "string") { - parts.push(encoder.encode(value)); - } else if (value instanceof ArrayBuffer) { - parts.push(new Uint8Array(value)); - } else if (!value) { - // noop for undefined - } else { - reject("unhandled type on stream read"); - } + while (true) { + const { done, value } = await stream.read(); + + if (done) break; + + if (typeof value === "string") { + parts.push(encoder.encode(value)); + } else if (value instanceof ArrayBuffer) { + parts.push(new Uint8Array(value)); + } else if (!value) { + // noop for undefined + } else { + throw new Error("unhandled type on stream read"); + } + } - return pump(); - }) - .catch((err): void => { - reject(err); - }); - })(); - }); + return concatenate(...parts); } export const BodyUsedError = From 1c4a9665e2a2ff85ccb8060f168dafafa4d2194b Mon Sep 17 00:00:00 2001 From: Marcos Casagrande Date: Mon, 25 May 2020 15:26:36 +0200 Subject: [PATCH 15/30] fix: Allow ArrayBuffer as Fetch request body (#5831) --- cli/js/web/fetch.ts | 2 ++ cli/tests/unit/fetch_test.ts | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/cli/js/web/fetch.ts b/cli/js/web/fetch.ts index 3485a770acd5ce..9054465bf4810f 100644 --- a/cli/js/web/fetch.ts +++ b/cli/js/web/fetch.ts @@ -505,6 +505,8 @@ export async function fetch( contentType = "text/plain;charset=UTF-8"; } else if (isTypedArray(init.body)) { body = init.body; + } else if (init.body instanceof ArrayBuffer) { + body = new Uint8Array(init.body); } else if (init.body instanceof URLSearchParams) { body = new TextEncoder().encode(init.body.toString()); contentType = "application/x-www-form-urlencoded;charset=UTF-8"; diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts index 458c34ae23dc1e..db4c3a407c35ae 100644 --- a/cli/tests/unit/fetch_test.ts +++ b/cli/tests/unit/fetch_test.ts @@ -258,6 +258,19 @@ unitTest( } ); +unitTest( + { perms: { net: true } }, + async function fetchInitArrayBufferBody(): Promise { + const data = "Hello World"; + const response = await fetch("http://localhost:4545/echo_server", { + method: "POST", + body: new TextEncoder().encode(data).buffer, + }); + const text = await response.text(); + assertEquals(text, data); + } +); + unitTest( { perms: { net: true } }, async function fetchInitURLSearchParamsBody(): Promise { From aef9f22462d287a5d18e517aabd8f03210d159b8 Mon Sep 17 00:00:00 2001 From: Rares Folea <30867783+raresraf@users.noreply.github.com> Date: Mon, 25 May 2020 16:35:11 +0300 Subject: [PATCH 16/30] Fix typo (#5834) --- std/node/_fs/_fs_copy_test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/std/node/_fs/_fs_copy_test.ts b/std/node/_fs/_fs_copy_test.ts index 9a27f7a83fe66a..f7ce0e279ec5b6 100644 --- a/std/node/_fs/_fs_copy_test.ts +++ b/std/node/_fs/_fs_copy_test.ts @@ -10,13 +10,13 @@ const destFile = "./destination.txt"; test({ name: "[std/node/fs] copy file", fn: async () => { - const srouceFile = Deno.makeTempFileSync(); + const sourceFile = Deno.makeTempFileSync(); const err = await new Promise((resolve) => { - copyFile(srouceFile, destFile, (err?: Error | null) => resolve(err)); + copyFile(sourceFile, destFile, (err?: Error | null) => resolve(err)); }); assert(!err); assert(existsSync(destFile)); - Deno.removeSync(srouceFile); + Deno.removeSync(sourceFile); Deno.removeSync(destFile); }, }); @@ -24,10 +24,10 @@ test({ test({ name: "[std/node/fs] copy file sync", fn: () => { - const srouceFile = Deno.makeTempFileSync(); - copyFileSync(srouceFile, destFile); + const sourceFile = Deno.makeTempFileSync(); + copyFileSync(sourceFile, destFile); assert(existsSync(destFile)); - Deno.removeSync(srouceFile); + Deno.removeSync(sourceFile); Deno.removeSync(destFile); }, }); From 08f74e1f6a180e83e13f5570811b8b7fcec90e9f Mon Sep 17 00:00:00 2001 From: Marcos Casagrande Date: Mon, 25 May 2020 18:55:16 +0200 Subject: [PATCH 17/30] fix(cli/web/fetch): Make Response constructor standard (#5787) --- cli/js/lib.deno.shared_globals.d.ts | 21 +- cli/js/web/body.ts | 21 +- cli/js/web/dom_types.d.ts | 1 - cli/js/web/fetch.ts | 506 +++++++--------------------- cli/js/web/request.ts | 2 +- cli/tests/unit/fetch_test.ts | 57 +++- 6 files changed, 191 insertions(+), 417 deletions(-) diff --git a/cli/js/lib.deno.shared_globals.d.ts b/cli/js/lib.deno.shared_globals.d.ts index ed1c1ac0b5dbdc..f3a565668654e2 100644 --- a/cli/js/lib.deno.shared_globals.d.ts +++ b/cli/js/lib.deno.shared_globals.d.ts @@ -922,6 +922,12 @@ declare const Request: { new (input: RequestInfo, init?: RequestInit): Request; }; +interface ResponseInit { + headers?: HeadersInit; + status?: number; + statusText?: string; +} + type ResponseType = | "basic" | "cors" @@ -945,20 +951,7 @@ interface Response extends Body { declare const Response: { prototype: Response; - - // TODO(#4667) Response constructor is non-standard. - // new(body?: BodyInit | null, init?: ResponseInit): Response; - new ( - url: string, - status: number, - statusText: string, - headersList: Array<[string, string]>, - rid: number, - redirected_: boolean, - type_?: null | ResponseType, - body_?: null | Body - ): Response; - + new (body?: BodyInit | null, init?: ResponseInit): Response; error(): Response; redirect(url: string, status?: number): Response; }; diff --git a/cli/js/web/body.ts b/cli/js/web/body.ts index f0d88be4b42e5f..9c4997755c967d 100644 --- a/cli/js/web/body.ts +++ b/cli/js/web/body.ts @@ -8,15 +8,7 @@ import { getHeaderValueParams, hasHeaderValueOf } from "./util.ts"; const { TextEncoder, TextDecoder } = encoding; const DenoBlob = blob.DenoBlob; -export type BodySource = - | Blob - | BufferSource - | FormData - | URLSearchParams - | ReadableStream - | string; - -function validateBodyType(owner: Body, bodySource: BodySource): boolean { +function validateBodyType(owner: Body, bodySource: BodyInit | null): boolean { if ( bodySource instanceof Int8Array || bodySource instanceof Int16Array || @@ -74,6 +66,8 @@ async function bufferFromStream( parts.push(encoder.encode(value)); } else if (value instanceof ArrayBuffer) { parts.push(new Uint8Array(value)); + } else if (value instanceof Uint8Array) { + parts.push(value); } else if (!value) { // noop for undefined } else { @@ -90,7 +84,10 @@ export const BodyUsedError = export class Body implements domTypes.Body { protected _stream: ReadableStreamImpl | null; - constructor(protected _bodySource: BodySource, readonly contentType: string) { + constructor( + protected _bodySource: BodyInit | null, + readonly contentType: string + ) { validateBodyType(this, _bodySource); this._bodySource = _bodySource; this.contentType = contentType; @@ -126,7 +123,9 @@ export class Body implements domTypes.Body { } public async blob(): Promise { - return new DenoBlob([await this.arrayBuffer()]); + return new DenoBlob([await this.arrayBuffer()], { + type: this.contentType, + }); } // ref: https://fetch.spec.whatwg.org/#body-mixin diff --git a/cli/js/web/dom_types.d.ts b/cli/js/web/dom_types.d.ts index b5b172ccde68c2..5d35c91875147c 100644 --- a/cli/js/web/dom_types.d.ts +++ b/cli/js/web/dom_types.d.ts @@ -305,7 +305,6 @@ export interface Response extends Body { readonly redirected: boolean; readonly status: number; readonly statusText: string; - readonly trailer: Promise; readonly type: ResponseType; readonly url: string; clone(): Response; diff --git a/cli/js/web/fetch.ts b/cli/js/web/fetch.ts index 9054465bf4810f..ec75d67cbe1e8d 100644 --- a/cli/js/web/fetch.ts +++ b/cli/js/web/fetch.ts @@ -1,312 +1,76 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { assert, createResolvable, notImplemented } from "../util.ts"; +import { notImplemented } from "../util.ts"; import { isTypedArray } from "./util.ts"; import * as domTypes from "./dom_types.d.ts"; import { TextDecoder, TextEncoder } from "./text_encoding.ts"; import { DenoBlob, bytesSymbol as blobBytesSymbol } from "./blob.ts"; -import * as io from "../io.ts"; import { read } from "../ops/io.ts"; import { close } from "../ops/resources.ts"; -import { Buffer } from "../buffer.ts"; import { fetch as opFetch, FetchResponse } from "../ops/fetch.ts"; +import * as Body from "./body.ts"; import { DomFileImpl } from "./dom_file.ts"; -import { getHeaderValueParams, hasHeaderValueOf } from "./util.ts"; - -class Body - implements domTypes.Body, ReadableStream, io.Reader, io.Closer { - #bodyUsed = false; - #bodyPromise: Promise | null = null; - #data: ArrayBuffer | null = null; - #rid: number; - readonly locked: boolean = false; // TODO - readonly body: ReadableStream; - - constructor(rid: number, readonly contentType: string) { - this.#rid = rid; - this.body = this; - } - - #bodyBuffer = async (): Promise => { - assert(this.#bodyPromise == null); - const buf = new Buffer(); - try { - const nread = await buf.readFrom(this); - const ui8 = buf.bytes(); - assert(ui8.byteLength === nread); - this.#data = ui8.buffer.slice( - ui8.byteOffset, - ui8.byteOffset + nread - ) as ArrayBuffer; - assert(this.#data.byteLength === nread); - } finally { - this.close(); - } +import { getHeaderValueParams } from "./util.ts"; +import { ReadableStreamImpl } from "./streams/readable_stream.ts"; - return this.#data; - }; +const responseData = new WeakMap(); +export class Response extends Body.Body implements domTypes.Response { + readonly type: ResponseType; + readonly redirected: boolean; + readonly url: string; + readonly status: number; + readonly statusText: string; + headers: Headers; - // eslint-disable-next-line require-await - async arrayBuffer(): Promise { - // If we've already bufferred the response, just return it. - if (this.#data != null) { - return this.#data; - } + constructor(body: BodyInit | null = null, init?: domTypes.ResponseInit) { + init = init ?? {}; - // If there is no _bodyPromise yet, start it. - if (this.#bodyPromise == null) { - this.#bodyPromise = this.#bodyBuffer(); + if (typeof init !== "object") { + throw new TypeError(`'init' is not an object`); } - return this.#bodyPromise; - } + const extraInit = responseData.get(init) || {}; + let { type = "default", url = "" } = extraInit; - async blob(): Promise { - const arrayBuffer = await this.arrayBuffer(); - return new DenoBlob([arrayBuffer], { - type: this.contentType, - }); - } + let status = (Number(init.status) || 0) ?? 200; + let statusText = init.statusText ?? ""; + let headers = + init.headers instanceof Headers + ? init.headers + : new Headers(init.headers); - // ref: https://fetch.spec.whatwg.org/#body-mixin - async formData(): Promise { - const formData = new FormData(); - const enc = new TextEncoder(); - if (hasHeaderValueOf(this.contentType, "multipart/form-data")) { - const params = getHeaderValueParams(this.contentType); - if (!params.has("boundary")) { - // TypeError is required by spec - throw new TypeError("multipart/form-data must provide a boundary"); - } - // ref: https://tools.ietf.org/html/rfc2046#section-5.1 - const boundary = params.get("boundary")!; - const dashBoundary = `--${boundary}`; - const delimiter = `\r\n${dashBoundary}`; - const closeDelimiter = `${delimiter}--`; - - const body = await this.text(); - let bodyParts: string[]; - const bodyEpilogueSplit = body.split(closeDelimiter); - if (bodyEpilogueSplit.length < 2) { - bodyParts = []; - } else { - // discard epilogue - const bodyEpilogueTrimmed = bodyEpilogueSplit[0]; - // first boundary treated special due to optional prefixed \r\n - const firstBoundaryIndex = bodyEpilogueTrimmed.indexOf(dashBoundary); - if (firstBoundaryIndex < 0) { - throw new TypeError("Invalid boundary"); - } - const bodyPreambleTrimmed = bodyEpilogueTrimmed - .slice(firstBoundaryIndex + dashBoundary.length) - .replace(/^[\s\r\n\t]+/, ""); // remove transport-padding CRLF - // trimStart might not be available - // Be careful! body-part allows trailing \r\n! - // (as long as it is not part of `delimiter`) - bodyParts = bodyPreambleTrimmed - .split(delimiter) - .map((s): string => s.replace(/^[\s\r\n\t]+/, "")); - // TODO: LWSP definition is actually trickier, - // but should be fine in our case since without headers - // we should just discard the part - } - for (const bodyPart of bodyParts) { - const headers = new Headers(); - const headerOctetSeperatorIndex = bodyPart.indexOf("\r\n\r\n"); - if (headerOctetSeperatorIndex < 0) { - continue; // Skip unknown part - } - const headerText = bodyPart.slice(0, headerOctetSeperatorIndex); - const octets = bodyPart.slice(headerOctetSeperatorIndex + 4); - - // TODO: use textproto.readMIMEHeader from deno_std - const rawHeaders = headerText.split("\r\n"); - for (const rawHeader of rawHeaders) { - const sepIndex = rawHeader.indexOf(":"); - if (sepIndex < 0) { - continue; // Skip this header - } - const key = rawHeader.slice(0, sepIndex); - const value = rawHeader.slice(sepIndex + 1); - headers.set(key, value); - } - if (!headers.has("content-disposition")) { - continue; // Skip unknown part - } - // Content-Transfer-Encoding Deprecated - const contentDisposition = headers.get("content-disposition")!; - const partContentType = headers.get("content-type") || "text/plain"; - // TODO: custom charset encoding (needs TextEncoder support) - // const contentTypeCharset = - // getHeaderValueParams(partContentType).get("charset") || ""; - if (!hasHeaderValueOf(contentDisposition, "form-data")) { - continue; // Skip, might not be form-data - } - const dispositionParams = getHeaderValueParams(contentDisposition); - if (!dispositionParams.has("name")) { - continue; // Skip, unknown name - } - const dispositionName = dispositionParams.get("name")!; - if (dispositionParams.has("filename")) { - const filename = dispositionParams.get("filename")!; - const blob = new DenoBlob([enc.encode(octets)], { - type: partContentType, - }); - // TODO: based on spec - // https://xhr.spec.whatwg.org/#dom-formdata-append - // https://xhr.spec.whatwg.org/#create-an-entry - // Currently it does not mention how I could pass content-type - // to the internally created file object... - formData.append(dispositionName, blob, filename); - } else { - formData.append(dispositionName, octets); - } - } - return formData; - } else if ( - hasHeaderValueOf(this.contentType, "application/x-www-form-urlencoded") - ) { - // From https://github.com/github/fetch/blob/master/fetch.js - // Copyright (c) 2014-2016 GitHub, Inc. MIT License - const body = await this.text(); - try { - body - .trim() - .split("&") - .forEach((bytes): void => { - if (bytes) { - const split = bytes.split("="); - const name = split.shift()!.replace(/\+/g, " "); - const value = split.join("=").replace(/\+/g, " "); - formData.append( - decodeURIComponent(name), - decodeURIComponent(value) - ); - } - }); - } catch (e) { - throw new TypeError("Invalid form urlencoded format"); - } - return formData; - } else { - throw new TypeError("Invalid form data"); + if (init.status && (status < 200 || status > 599)) { + throw new RangeError( + `The status provided (${init.status}) is outside the range [200, 599]` + ); } - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - async json(): Promise { - const text = await this.text(); - return JSON.parse(text); - } - - async text(): Promise { - const ab = await this.arrayBuffer(); - const decoder = new TextDecoder("utf-8"); - return decoder.decode(ab); - } - - read(p: Uint8Array): Promise { - this.#bodyUsed = true; - return read(this.#rid, p); - } - - close(): Promise { - close(this.#rid); - return Promise.resolve(); - } - - cancel(): Promise { - return notImplemented(); - } - - getIterator(_options?: { - preventCancel?: boolean; - }): AsyncIterableIterator { - return notImplemented(); - } - - getReader(): ReadableStreamDefaultReader { - return notImplemented(); - } - - tee(): [ReadableStream, ReadableStream] { - return notImplemented(); - } - [Symbol.asyncIterator](): AsyncIterableIterator { - return io.iter(this); - } - - get bodyUsed(): boolean { - return this.#bodyUsed; - } - - pipeThrough( - _: { - writable: WritableStream; - readable: ReadableStream; - }, - _options?: PipeOptions - ): ReadableStream { - return notImplemented(); - } - - pipeTo( - _dest: WritableStream, - _options?: PipeOptions - ): Promise { - return notImplemented(); - } -} - -export class Response implements domTypes.Response { - readonly type: ResponseType; - readonly redirected: boolean; - headers: Headers; - readonly trailer: Promise; - readonly body: Body | null; - - constructor( - readonly url: string, - readonly status: number, - readonly statusText: string, - headersList: Array<[string, string]>, - rid: number, - redirected_: boolean, - readonly type_: null | ResponseType = "default", - body_: null | Body = null - ) { - this.trailer = createResolvable(); - this.headers = new Headers(headersList); - const contentType = this.headers.get("content-type") || ""; - - if (body_ == null) { - this.body = new Body(rid, contentType); - } else { - this.body = body_; + // null body status + if (body && [/* 101, */ 204, 205, 304].includes(status)) { + throw new TypeError("Response with null body status cannot have body"); } - if (type_ == null) { - this.type = "default"; + if (!type) { + type = "default"; } else { - this.type = type_; - if (type_ == "error") { + type = type; + if (type == "error") { // spec: https://fetch.spec.whatwg.org/#concept-network-error - this.status = 0; - this.statusText = ""; - this.headers = new Headers(); - this.body = null; + status = 0; + statusText = ""; + headers = new Headers(); + body = null; /* spec for other Response types: https://fetch.spec.whatwg.org/#concept-filtered-response-basic Please note that type "basic" is not the same thing as "default".*/ - } else if (type_ == "basic") { - for (const h of this.headers) { + } else if (type == "basic") { + for (const h of headers) { /* Forbidden Response-Header Names: https://fetch.spec.whatwg.org/#forbidden-response-header-name */ if (["set-cookie", "set-cookie2"].includes(h[0].toLowerCase())) { - this.headers.delete(h[0]); + headers.delete(h[0]); } } - } else if (type_ == "cors") { + } else if (type == "cors") { /* CORS-safelisted Response-Header Names: https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name */ const allowedHeaders = [ @@ -318,7 +82,7 @@ export class Response implements domTypes.Response { "Last-Modified", "Pragma", ].map((c: string) => c.toLowerCase()); - for (const h of this.headers) { + for (const h of headers) { /* Technically this is still not standards compliant because we are supposed to allow headers allowed in the 'Access-Control-Expose-Headers' header in the 'internal response' @@ -328,87 +92,39 @@ export class Response implements domTypes.Response { TODO(serverhiccups): change how internal responses are handled so we can do this properly. */ if (!allowedHeaders.includes(h[0].toLowerCase())) { - this.headers.delete(h[0]); + headers.delete(h[0]); } } /* TODO(serverhiccups): Once I fix the 'internal response' thing, these actually need to treat the internal response differently */ - } else if (type_ == "opaque" || type_ == "opaqueredirect") { - this.url = ""; - this.status = 0; - this.statusText = ""; - this.headers = new Headers(); - this.body = null; + } else if (type == "opaque" || type == "opaqueredirect") { + url = ""; + status = 0; + statusText = ""; + headers = new Headers(); + body = null; } } - this.redirected = redirected_; - } - - #bodyViewable = (): boolean => { - if ( - this.type == "error" || - this.type == "opaque" || - this.type == "opaqueredirect" || - this.body == undefined - ) { - return true; - } - return false; - }; - - arrayBuffer(): Promise { - /* You have to do the null check here and not in the function because - * otherwise TS complains about this.body potentially being null */ - if (this.#bodyViewable() || this.body == null) { - return Promise.reject(new Error("Response body is null")); - } - return this.body.arrayBuffer(); - } - - blob(): Promise { - if (this.#bodyViewable() || this.body == null) { - return Promise.reject(new Error("Response body is null")); - } - return this.body.blob(); - } - - formData(): Promise { - if (this.#bodyViewable() || this.body == null) { - return Promise.reject(new Error("Response body is null")); - } - return this.body.formData(); - } + const contentType = headers.get("content-type") || ""; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - json(): Promise { - if (this.#bodyViewable() || this.body == null) { - return Promise.reject(new Error("Response body is null")); - } - return this.body.json(); - } + super(body, contentType); - text(): Promise { - if (this.#bodyViewable() || this.body == null) { - return Promise.reject(new Error("Response body is null")); - } - return this.body.text(); + this.url = url; + this.statusText = statusText; + this.status = status; + this.headers = headers; + this.redirected = extraInit.redirected; + this.type = type; } get ok(): boolean { return 200 <= this.status && this.status < 300; } - get bodyUsed(): boolean { - if (this.body === null) return false; - return this.body.bodyUsed; - } - - clone(): domTypes.Response { + public clone(): domTypes.Response { if (this.bodyUsed) { - throw new TypeError( - "Failed to execute 'clone' on 'Response': Response body is already used" - ); + throw TypeError(Body.BodyUsedError); } const iterators = this.headers.entries(); @@ -417,16 +133,20 @@ export class Response implements domTypes.Response { headersList.push(header); } - return new Response( - this.url, - this.status, - this.statusText, - headersList, - -1, - this.redirected, - this.type, - this.body - ); + let resBody = this._bodySource; + + if (this._bodySource instanceof ReadableStreamImpl) { + const tees = this._bodySource.tee(); + this._stream = this._bodySource = tees[0]; + resBody = tees[1]; + } + + const cloned = new Response(resBody, { + status: this.status, + statusText: this.statusText, + headers: new Headers(headersList), + }); + return cloned; } static redirect(url: URL | string, status: number): domTypes.Response { @@ -435,16 +155,11 @@ export class Response implements domTypes.Response { "The redirection status must be one of 301, 302, 303, 307 and 308." ); } - return new Response( - "", + return new Response(null, { status, - "", - [["Location", typeof url === "string" ? url : url.toString()]], - -1, - false, - "default", - null - ); + statusText: "", + headers: [["Location", typeof url === "string" ? url : url.toString()]], + }); } } @@ -576,26 +291,65 @@ export async function fetch( while (remRedirectCount) { const fetchResponse = await sendFetchReq(url, method, headers, body); - const response = new Response( + const responseBody = new ReadableStreamImpl({ + async pull(controller: ReadableStreamDefaultController): Promise { + try { + const b = new Uint8Array(1024 * 32); + const result = await read(fetchResponse.bodyRid, b); + if (result === null) { + controller.close(); + return close(fetchResponse.bodyRid); + } + + controller.enqueue(b.subarray(0, result)); + } catch (e) { + controller.error(e); + controller.close(); + close(fetchResponse.bodyRid); + } + }, + cancel(): void { + // When reader.cancel() is called + close(fetchResponse.bodyRid); + }, + }); + + let responseInit: ResponseInit = { + status: fetchResponse.status, + statusText: fetchResponse.statusText, + headers: fetchResponse.headers, + }; + + responseData.set(responseInit, { + redirected, + rid: fetchResponse.bodyRid, url, - fetchResponse.status, - fetchResponse.statusText, - fetchResponse.headers, - fetchResponse.bodyRid, - redirected - ); - if ([301, 302, 303, 307, 308].includes(response.status)) { + }); + + const response = new Response(responseBody, responseInit); + + if ([301, 302, 303, 307, 308].includes(fetchResponse.status)) { // We won't use body of received response, so close it now // otherwise it will be kept in resource table. close(fetchResponse.bodyRid); // We're in a redirect status switch ((init && init.redirect) || "follow") { case "error": - /* I suspect that deno will probably crash if you try to use that - rid, which suggests to me that Response needs to be refactored */ - return new Response("", 0, "", [], -1, false, "error", null); + responseInit = {}; + responseData.set(responseInit, { + type: "error", + redirected: false, + url: "", + }); + return new Response(null, responseInit); case "manual": - return new Response("", 0, "", [], -1, false, "opaqueredirect", null); + responseInit = {}; + responseData.set(responseInit, { + type: "opaqueredirect", + redirected: false, + url: "", + }); + return new Response(null, responseInit); case "follow": default: let redirectUrl = response.headers.get("Location"); diff --git a/cli/js/web/request.ts b/cli/js/web/request.ts index 8fe93babe83479..286aaff56eb0fa 100644 --- a/cli/js/web/request.ts +++ b/cli/js/web/request.ts @@ -39,7 +39,7 @@ export class Request extends body.Body implements domTypes.Request { init = {}; } - let b: body.BodySource; + let b: BodyInit; // prefer body from init if (init.body) { diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts index db4c3a407c35ae..e829ddc60ac088 100644 --- a/cli/tests/unit/fetch_test.ts +++ b/cli/tests/unit/fetch_test.ts @@ -118,6 +118,49 @@ unitTest({ perms: { net: true } }, async function fetchAsyncIterator(): Promise< }); */ +unitTest({ perms: { net: true } }, async function fetchBodyReader(): Promise< + void +> { + const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); + const headers = response.headers; + assert(response.body !== null); + const reader = await response.body.getReader(); + let total = 0; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + assert(value); + total += value.length; + } + + assertEquals(total, Number(headers.get("Content-Length"))); +}); + +unitTest( + { perms: { net: true } }, + async function fetchBodyReaderBigBody(): Promise { + const data = "a".repeat(10 << 10); // 10mb + const response = await fetch( + "http://localhost:4545/cli/tests/echo_server", + { + method: "POST", + body: data, + } + ); + assert(response.body !== null); + const reader = await response.body.getReader(); + let total = 0; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + assert(value); + total += value.length; + } + + assertEquals(total, data.length); + } +); + unitTest({ perms: { net: true } }, async function responseClone(): Promise< void > { @@ -538,17 +581,3 @@ unitTest(function responseRedirect(): void { assertEquals(redir.headers.get("Location"), "example.com/newLocation"); assertEquals(redir.type, "default"); }); - -unitTest(function responseConstructionHeaderRemoval(): void { - const res = new Response( - "example.com", - 200, - "OK", - [["Set-Cookie", "mysessionid"]], - -1, - false, - "basic", - null - ); - assert(res.headers.get("Set-Cookie") != "mysessionid"); -}); From 4ebd24342368adbb99582b87dc6c4b8cb6f44c87 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Mon, 25 May 2020 18:32:34 +0100 Subject: [PATCH 18/30] fix(std/testing/asserts): Support browsers (#5847) --- docs/contributing/style_guide.md | 13 +++++++++++++ std/fmt/colors.ts | 9 +++++---- std/testing/asserts.ts | 9 ++++++--- std/testing/asserts_test.ts | 2 +- std/testing/diff.ts | 2 ++ 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/docs/contributing/style_guide.md b/docs/contributing/style_guide.md index ed23f31e13b8e1..0bd7c628f34bb7 100644 --- a/docs/contributing/style_guide.md +++ b/docs/contributing/style_guide.md @@ -314,3 +314,16 @@ export function foo(): string { `https://deno.land/std/` is intended to be baseline functionality that all Deno programs can rely on. We want to guarantee to users that this code does not include potentially unreviewed third party code. + +#### Document and maintain browser compatiblity. + +If a module is browser compatible, include the following in the JSDoc at the top +of the module: + +```ts +/** This module is browser compatible. */ +``` + +Maintain browser compatibility for such a module by either not using the global +`Deno` namespace or feature-testing for it. Make sure any new dependencies are +also browser compatible. diff --git a/std/fmt/colors.ts b/std/fmt/colors.ts index 6a06af20e057e6..a020657d990d91 100644 --- a/std/fmt/colors.ts +++ b/std/fmt/colors.ts @@ -1,6 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -/** - * A module to print ANSI terminal colors. Inspired by chalk, kleur, and colors +/** A module to print ANSI terminal colors. Inspired by chalk, kleur, and colors * on npm. * * ``` @@ -10,8 +9,10 @@ * * This module supports `NO_COLOR` environmental variable disabling any coloring * if `NO_COLOR` is set. - */ -const { noColor } = Deno; + * + * This module is browser compatible. */ + +const noColor = globalThis.Deno?.noColor ?? true; interface Code { open: string; diff --git a/std/testing/asserts.ts b/std/testing/asserts.ts index d3f8bb678497c4..ce721499847771 100644 --- a/std/testing/asserts.ts +++ b/std/testing/asserts.ts @@ -1,4 +1,7 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +/** This module is browser compatible. Do not rely on good formatting of values + * for AssertionError messages in browsers. */ + import { red, green, white, gray, bold } from "../fmt/colors.ts"; import diff, { DiffType, DiffResult } from "./diff.ts"; @@ -17,7 +20,7 @@ export class AssertionError extends Error { } function format(v: unknown): string { - let string = Deno.inspect(v); + let string = globalThis.Deno ? Deno.inspect(v) : String(v); if (typeof v == "string") { string = `"${string.replace(/(?=["\\])/g, "\\")}"`; } @@ -254,7 +257,7 @@ export function assertStrContains( ): void { if (!actual.includes(expected)) { if (!msg) { - msg = `actual: "${actual}" expected to contains: "${expected}"`; + msg = `actual: "${actual}" expected to contain: "${expected}"`; } throw new AssertionError(msg); } @@ -286,7 +289,7 @@ export function assertArrayContains( return; } if (!msg) { - msg = `actual: "${actual}" expected to contains: "${expected}"`; + msg = `actual: "${actual}" expected to contain: "${expected}"`; msg += "\n"; msg += `missing: ${missing}`; } diff --git a/std/testing/asserts_test.ts b/std/testing/asserts_test.ts index 14eabca610f741..fb25d46cf74b9a 100644 --- a/std/testing/asserts_test.ts +++ b/std/testing/asserts_test.ts @@ -169,7 +169,7 @@ test("testingAssertStringContainsThrow", function (): void { } catch (e) { assert( e.message === - `actual: "Denosaurus from Jurassic" expected to contains: "Raptor"` + `actual: "Denosaurus from Jurassic" expected to contain: "Raptor"` ); assert(e instanceof AssertionError); didThrow = true; diff --git a/std/testing/diff.ts b/std/testing/diff.ts index 97baa089f43e21..da1e827ac04545 100644 --- a/std/testing/diff.ts +++ b/std/testing/diff.ts @@ -1,4 +1,6 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +/** This module is browser compatible. */ + interface FarthestPoint { y: number; id: number; From 4e92ef7dc9e1d223d9f3099b94579c2b17e4ef9e Mon Sep 17 00:00:00 2001 From: Marcos Casagrande Date: Mon, 25 May 2020 22:20:09 +0200 Subject: [PATCH 19/30] Add more tests for fetch response body (#5852) --- cli/tests/unit/fetch_test.ts | 107 +++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts index e829ddc60ac088..ed17c869a3b13c 100644 --- a/cli/tests/unit/fetch_test.ts +++ b/cli/tests/unit/fetch_test.ts @@ -581,3 +581,110 @@ unitTest(function responseRedirect(): void { assertEquals(redir.headers.get("Location"), "example.com/newLocation"); assertEquals(redir.type, "default"); }); + +unitTest({ perms: { net: true } }, async function fetchBodyReadTwice(): Promise< + void +> { + const response = await fetch("http://localhost:4545/cli/tests/fixture.json"); + + // Read body + const _json = await response.json(); + assert(_json); + + // All calls after the body was consumed, should fail + const methods = ["json", "text", "formData", "arrayBuffer"]; + for (const method of methods) { + try { + // @ts-ignore + await response[method](); + fail( + "Reading body multiple times should failed, the stream should've been locked." + ); + } catch {} + } +}); + +unitTest( + { perms: { net: true } }, + async function fetchBodyReaderAfterRead(): Promise { + const response = await fetch( + "http://localhost:4545/cli/tests/fixture.json" + ); + assert(response.body !== null); + const reader = await response.body.getReader(); + while (true) { + const { done, value } = await reader.read(); + if (done) break; + assert(value); + } + + try { + response.body.getReader(); + fail("The stream should've been locked."); + } catch {} + } +); + +unitTest( + { perms: { net: true } }, + async function fetchBodyReaderWithCancelAndNewReader(): Promise { + const data = "a".repeat(1 << 10); + const response = await fetch( + "http://localhost:4545/cli/tests/echo_server", + { + method: "POST", + body: data, + } + ); + assert(response.body !== null); + const firstReader = await response.body.getReader(); + + // Acquire reader without reading & release + await firstReader.releaseLock(); + + const reader = await response.body.getReader(); + + let total = 0; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + assert(value); + total += value.length; + } + + assertEquals(total, data.length); + } +); + +unitTest( + { perms: { net: true } }, + async function fetchBodyReaderWithReadCancelAndNewReader(): Promise { + const data = "a".repeat(1 << 10); + + const response = await fetch( + "http://localhost:4545/cli/tests/echo_server", + { + method: "POST", + body: data, + } + ); + assert(response.body !== null); + const firstReader = await response.body.getReader(); + + // Do one single read with first reader + const { value: firstValue } = await firstReader.read(); + assert(firstValue); + await firstReader.releaseLock(); + + // Continue read with second reader + const reader = await response.body.getReader(); + let total = firstValue.length || 0; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + assert(value); + total += value.length; + } + assertEquals(total, data.length); + } +); From 9090023c33de7b64ae41425db71c9ab4d5b1237f Mon Sep 17 00:00:00 2001 From: Chris Knight Date: Tue, 26 May 2020 12:12:07 +0100 Subject: [PATCH 20/30] docs: "Getting started" manual updates (#5835) --- docs/getting_started.md | 2 + docs/getting_started/first_steps.md | 14 +++--- docs/getting_started/installation.md | 13 +++++- docs/getting_started/permissions.md | 46 +++++++++++++++++-- .../getting_started/setup_your_environment.md | 4 +- docs/getting_started/typescript.md | 15 ++++-- 6 files changed, 77 insertions(+), 17 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 159d3de6486d1f..7737f08d746609 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -3,7 +3,9 @@ In this chapter we'll discuss: - Installing Deno +- Setting up your environment - Running a simple `Hello World` script - Writing our own script +- Understanding permissions - Using Deno with TypeScript - Using WebAssembly diff --git a/docs/getting_started/first_steps.md b/docs/getting_started/first_steps.md index 4b3dd51086a990..9186af7302ff41 100644 --- a/docs/getting_started/first_steps.md +++ b/docs/getting_started/first_steps.md @@ -14,7 +14,7 @@ before attempting to start with Deno. Deno is a runtime for JavaScript/TypeScript which tries to be web compatible and use modern features wherever possible. -Browser compatibility means, a simple `Hello World` program in Deno is the same +Browser compatibility means a simple `Hello World` program in Deno is the same as the one you can run in the browser: ```ts @@ -87,9 +87,9 @@ In this program each command-line argument is assumed to be a filename, the file is opened, and printed to stdout. ```ts -for (let i = 0; i < Deno.args.length; i++) { - let filename = Deno.args[i]; - let file = await Deno.open(filename); +const filenames = Deno.args; +for (const filename of filenames) { + const file = await Deno.open(filename); await Deno.copy(file, Deno.stdout); file.close(); } @@ -112,8 +112,10 @@ This is an example of a simple server which accepts connections on port 8080, and returns to the client anything it sends. ```ts -const listener = Deno.listen({ port: 8080 }); -console.log("listening on 0.0.0.0:8080"); +const hostname = "0.0.0.0"; +const port = 8080; +const listener = Deno.listen({ hostname, port }); +console.log(`Listening on ${hostname}:${port}`); for await (const conn of listener) { Deno.copy(conn, conn); } diff --git a/docs/getting_started/installation.md b/docs/getting_started/installation.md index 158e18133f20dd..7d39e4251675fa 100644 --- a/docs/getting_started/installation.md +++ b/docs/getting_started/installation.md @@ -59,11 +59,22 @@ Use `deno help` to see help text documenting Deno's flags and usage. Use ### Updating -To update a previously installed version of Deno, you can run `deno upgrade`. +To update a previously installed version of Deno, you can run: + +```shell +deno upgrade +``` + This will fetch the latest release from [github.com/denoland/deno/releases](https://github.com/denoland/deno/releases), unzip it, and replace your current executable with it. +You can also use this utility to install a specific version of Deno: + +```shell +deno upgrade --version 1.0.1 +``` + ### Building from source Information about how to build from source can be found in the `Contributing` diff --git a/docs/getting_started/permissions.md b/docs/getting_started/permissions.md index c5880225aca633..317d850463664d 100644 --- a/docs/getting_started/permissions.md +++ b/docs/getting_started/permissions.md @@ -1,15 +1,50 @@ ## Permissions - +Deno is secure by default. Therefore, unless you specifically enable it, a deno +module has no file, network, or environment access for example. Access to +security sensitive areas or functions requires the use of permissions to be +granted to a deno process on the command line. - +For the following example, `mod.ts` has been granted read-only access to the +file system. It cannot write to it, or perform any other security sensitive +functions. + +```shell +deno run --allow-read mod.ts +``` + +### Permissions list + +The following permissions are available: + +- **-A, --allow-all** Allow all permissions. This disables all security. +- **--allow-env** Allow environment access for things like getting and setting + of environment variables. +- **--allow-hrtime** Allow high resolution time measurement. High resolution + time can be used in timing attacks and fingerprinting. +- **--allow-net=\** Allow network access. You can specify an + optional, comma separated list of domains to provide a whitelist of allowed + domains. +- **--allow-plugin** Allow loading plugins. Please note that --allow-plugin is + an unstable feature. +- **--allow-read=\** Allow file system read access. You can specify + an optional, comma separated list of directories or files to provide a + whitelist of allowed file system access. +- **--allow-run** Allow running subprocesses. Be aware that subprocesses are not + run in a sandbox and therefore do not have the same security restrictions as + the deno process. Therefore, use with caution. +- **--allow-write=\** Allow file system write access. You can + specify an optional, comma separated list of directories or files to provide a + whitelist of allowed file system access. ### Permissions whitelist -Deno also allows you to control the granularity of permissions with whitelists. +Deno also allows you to control the granularity of some permissions with +whitelists. This example restricts file system access by whitelisting only the `/usr` -directory: +directory, however the execution fails as the process was attempting to access a +file in the `/etc` directory: ```shell $ deno run --allow-read=/usr https://deno.land/std/examples/cat.ts /etc/passwd @@ -41,6 +76,9 @@ This is an example on how to whitelist hosts/urls: $ deno run --allow-net=github.com,deno.land fetch.ts ``` +If `fetch.ts` tries to establish network connections to any other domain, the +process will fail. + Allow net calls to any host/url: ```shell diff --git a/docs/getting_started/setup_your_environment.md b/docs/getting_started/setup_your_environment.md index a0d04b3850b6bc..70bcb582112bcf 100644 --- a/docs/getting_started/setup_your_environment.md +++ b/docs/getting_started/setup_your_environment.md @@ -8,8 +8,8 @@ IDE of choice. There are several env vars that control how Deno behaves: -`DENO_DIR` defaults to `$HOME/.deno` but can be set to any path to control where -generated and cached source code is written and read to. +`DENO_DIR` defaults to `$HOME/.cache/deno` but can be set to any path to control +where generated and cached source code is written and read to. `NO_COLOR` will turn off color output if set. See https://no-color.org/. User code can test if `NO_COLOR` was set without having `--allow-env` by using the diff --git a/docs/getting_started/typescript.md b/docs/getting_started/typescript.md index 197c41482e2007..7689273db15437 100644 --- a/docs/getting_started/typescript.md +++ b/docs/getting_started/typescript.md @@ -2,12 +2,19 @@ -### Using external type definitions - Deno supports both JavaScript and TypeScript as first class languages at runtime. This means it requires fully qualified module names, including the extension (or a server providing the correct media type). In addition, Deno has -no "magical" module resolution. +no "magical" module resolution. Instead, imported modules are specified as files +(including extensions) or fully qualified URL imports. Typescript modules can be +directly imported. E.g. + +``` +import { Response } from "https://deno.land/std@0.53.0/http/server.ts"; +import { queue } from "./collections.ts"; +``` + +### Using external type definitions The out of the box TypeScript compiler though relies on both extension-less modules and the Node.js module resolution logic to apply types to JavaScript @@ -98,7 +105,7 @@ way to support customization a configuration file such as `tsconfig.json` might be provided to Deno on program execution. You need to explicitly tell Deno where to look for this configuration by setting -the `-c` argument when executing your application. +the `-c` (or `--config`) argument when executing your application. ```shell deno run -c tsconfig.json mod.ts From f462f7fe54d59b2d56ffbb03ca8467ce93096817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 26 May 2020 15:41:00 +0200 Subject: [PATCH 21/30] fix: parsing of JSX and TSX in SWC (#5870) --- cli/doc/parser.rs | 14 +++++--- cli/module_graph.rs | 21 ++++++++++++ cli/swc_util.rs | 65 ++++++++++++++++++++++++++++-------- cli/tests/ts_with_generic.ts | 3 ++ 4 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 cli/tests/ts_with_generic.ts diff --git a/cli/doc/parser.rs b/cli/doc/parser.rs index 0e58e4b0a655d8..9215637c50ac5b 100644 --- a/cli/doc/parser.rs +++ b/cli/doc/parser.rs @@ -1,4 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +use crate::file_fetcher::map_file_extension; use crate::op_error::OpError; use crate::swc_common::comments::CommentKind; use crate::swc_common::Span; @@ -15,6 +16,7 @@ use deno_core::ModuleSpecifier; use futures::Future; use regex::Regex; use std::collections::HashMap; +use std::path::PathBuf; use std::pin::Pin; use super::namespace::NamespaceDef; @@ -57,9 +59,12 @@ impl DocParser { file_name: &str, source_code: &str, ) -> Result { - self - .ast_parser - .parse_module(file_name, source_code, |parse_result| { + let media_type = map_file_extension(&PathBuf::from(file_name)); + self.ast_parser.parse_module( + file_name, + media_type, + source_code, + |parse_result| { let module = parse_result?; let doc_entries = self.get_doc_nodes_for_module_body(module.body.clone()); @@ -69,7 +74,8 @@ impl DocParser { reexports, }; Ok(module_doc) - }) + }, + ) } pub async fn parse(&self, file_name: &str) -> Result, ErrBox> { diff --git a/cli/module_graph.rs b/cli/module_graph.rs index 3a59a537d7f154..be3bd288462804 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -196,6 +196,7 @@ impl ModuleGraphLoader { let (import_descs, ref_descs) = analyze_dependencies_and_references( &specifier, + map_file_extension(&PathBuf::from(&specifier)), &source_code, self.analyze_dynamic_imports, )?; @@ -409,6 +410,7 @@ impl ModuleGraphLoader { let (import_descs, ref_descs) = analyze_dependencies_and_references( &module_specifier.to_string(), + source_file.media_type, &source_code, self.analyze_dynamic_imports, )?; @@ -786,4 +788,23 @@ mod tests { ); drop(http_server_guard); } + + #[tokio::test] + async fn source_graph_different_langs() { + let http_server_guard = crate::test_util::http_server(); + + // ModuleGraphLoader was mistakenly parsing this file as TSX + // https://github.com/denoland/deno/issues/5867 + + let module_specifier = ModuleSpecifier::resolve_url_or_path( + "http://localhost:4545/cli/tests/ts_with_generic.ts", + ) + .unwrap(); + + build_graph(&module_specifier) + .await + .expect("Failed to build graph"); + + drop(http_server_guard); + } } diff --git a/cli/swc_util.rs b/cli/swc_util.rs index 465fb9769e763c..ce737215911426 100644 --- a/cli/swc_util.rs +++ b/cli/swc_util.rs @@ -1,4 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +use crate::msg::MediaType; use crate::swc_common; use crate::swc_common::comments::CommentKind; use crate::swc_common::comments::Comments; @@ -13,19 +14,42 @@ use crate::swc_common::SourceMap; use crate::swc_common::Span; use crate::swc_ecma_ast; use crate::swc_ecma_parser::lexer::Lexer; +use crate::swc_ecma_parser::EsConfig; use crate::swc_ecma_parser::JscTarget; use crate::swc_ecma_parser::Parser; use crate::swc_ecma_parser::Session; use crate::swc_ecma_parser::SourceFileInput; use crate::swc_ecma_parser::Syntax; use crate::swc_ecma_parser::TsConfig; -use swc_ecma_visit::Node; -use swc_ecma_visit::Visit; - use std::error::Error; use std::fmt; use std::sync::Arc; use std::sync::RwLock; +use swc_ecma_visit::Node; +use swc_ecma_visit::Visit; + +fn get_default_es_config() -> EsConfig { + let mut config = EsConfig::default(); + config.num_sep = true; + config.class_private_props = false; + config.class_private_methods = false; + config.class_props = false; + config.export_default_from = true; + config.export_namespace_from = true; + config.dynamic_import = true; + config.nullish_coalescing = true; + config.optional_chaining = true; + config.import_meta = true; + config.top_level_await = true; + config +} + +fn get_default_ts_config() -> TsConfig { + let mut ts_config = TsConfig::default(); + ts_config.dynamic_import = true; + ts_config.decorators = true; + ts_config +} #[derive(Clone, Debug)] pub struct SwcDiagnosticBuffer { @@ -126,6 +150,7 @@ impl AstParser { pub fn parse_module( &self, file_name: &str, + media_type: MediaType, source_code: &str, callback: F, ) -> R @@ -143,12 +168,21 @@ impl AstParser { handler: &self.handler, }; - // TODO(bartlomieju): lexer should be configurable by the caller - let mut ts_config = TsConfig::default(); - ts_config.dynamic_import = true; - ts_config.decorators = true; - ts_config.tsx = true; - let syntax = Syntax::Typescript(ts_config); + let syntax = match media_type { + MediaType::JavaScript => Syntax::Es(get_default_es_config()), + MediaType::JSX => { + let mut config = get_default_es_config(); + config.jsx = true; + Syntax::Es(config) + } + MediaType::TypeScript => Syntax::Typescript(get_default_ts_config()), + MediaType::TSX => { + let mut config = get_default_ts_config(); + config.tsx = true; + Syntax::Typescript(config) + } + _ => Syntax::Es(get_default_es_config()), + }; let lexer = Lexer::new( session, @@ -433,6 +467,7 @@ pub struct TsReferenceDescriptor { pub fn analyze_dependencies_and_references( file_name: &str, + media_type: MediaType, source_code: &str, analyze_dynamic_imports: bool, ) -> Result< @@ -440,7 +475,7 @@ pub fn analyze_dependencies_and_references( SwcDiagnosticBuffer, > { let parser = AstParser::new(); - parser.parse_module(file_name, source_code, |parse_result| { + parser.parse_module(file_name, media_type, source_code, |parse_result| { let module = parse_result?; let mut collector = NewDependencyVisitor { dependencies: vec![], @@ -547,9 +582,13 @@ console.log(fizz); console.log(qat.qat); "#; - let (imports, references) = - analyze_dependencies_and_references("some/file.ts", source, true) - .expect("Failed to parse"); + let (imports, references) = analyze_dependencies_and_references( + "some/file.ts", + MediaType::TypeScript, + source, + true, + ) + .expect("Failed to parse"); assert_eq!( imports, diff --git a/cli/tests/ts_with_generic.ts b/cli/tests/ts_with_generic.ts new file mode 100644 index 00000000000000..aa83e73b9fb691 --- /dev/null +++ b/cli/tests/ts_with_generic.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ + +const foo = { delete() {} }; From 24c36fd8625df6b276108109478a62b4661a0b3f Mon Sep 17 00:00:00 2001 From: zfx <502545703@qq.com> Date: Tue, 26 May 2020 21:54:30 +0800 Subject: [PATCH 22/30] fix(std/log): use writeAllSync instead of writeSync (#5868) Deno.writeSync: Returns the number of bytes written. It is not guaranteed that the full buffer will be written in a single call. --- std/log/handlers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/log/handlers.ts b/std/log/handlers.ts index b22f458ac240c5..ffcd846cc69164 100644 --- a/std/log/handlers.ts +++ b/std/log/handlers.ts @@ -125,7 +125,7 @@ export class FileHandler extends WriterHandler { } log(msg: string): void { - Deno.writeSync(this._file.rid, this.#encoder.encode(msg + "\n")); + Deno.writeAllSync(this._file, this.#encoder.encode(msg + "\n")); } destroy(): Promise { From 228f9c207f8320908325a553c96e465da08fc617 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Wed, 27 May 2020 00:02:16 +1000 Subject: [PATCH 23/30] Use ts-expect-error instead of ts-ignore. (#5869) --- cli/js/error_stack.ts | 8 ++++---- cli/js/repl.ts | 2 +- cli/js/runtime_main.ts | 4 ++-- cli/js/runtime_worker.ts | 4 ++-- cli/js/testing.ts | 4 +--- cli/js/web/body.ts | 2 -- cli/js/web/console.ts | 12 ++++++------ cli/js/web/fetch.ts | 2 +- cli/js/web/timers.ts | 4 ++-- cli/tests/042_dyn_import_evalcontext.ts | 2 +- cli/tests/unit/body_test.ts | 4 ++-- cli/tests/unit/console_test.ts | 5 ++--- cli/tests/unit/dispatch_json_test.ts | 4 ++-- cli/tests/unit/dispatch_minimal_test.ts | 4 ++-- cli/tests/unit/dom_iterable_test.ts | 2 +- cli/tests/unit/error_stack_test.ts | 2 +- cli/tests/unit/event_target_test.ts | 6 ------ cli/tests/unit/fetch_test.ts | 4 ++-- cli/tests/unit/files_test.ts | 4 ++-- cli/tests/unit/form_data_test.ts | 14 +++++++------- cli/tests/unit/globals_test.ts | 16 ++++++++-------- cli/tests/unit/headers_test.ts | 12 ++++++------ cli/tests/unit/internals_test.ts | 2 +- cli/tests/unit/request_test.ts | 8 ++++---- cli/tests/unit/streams_internal_test.ts | 12 ++++++------ cli/tests/unit/unit_test_runner.ts | 2 +- cli/tests/unit/url_search_params_test.ts | 6 +++--- std/http/server_test.ts | 1 - std/mime/multipart_test.ts | 2 +- std/node/global.ts | 2 +- std/node/module.ts | 14 +++++++------- std/signal/test.ts | 2 +- std/uuid/tests/isNil.ts | 1 - std/ws/mod.ts | 2 +- 34 files changed, 81 insertions(+), 94 deletions(-) diff --git a/cli/js/error_stack.ts b/cli/js/error_stack.ts index e77f0865c995e0..8a3d0530b05bf3 100644 --- a/cli/js/error_stack.ts +++ b/cli/js/error_stack.ts @@ -244,19 +244,19 @@ function prepareStackTrace( } ) .map((callSite): string => { - // @ts-ignore + // @ts-expect-error error.__callSiteEvals.push(Object.freeze(evaluateCallSite(callSite))); const isInternal = callSite.getFileName()?.startsWith("$deno$") ?? false; const string = callSiteToString(callSite, isInternal); - // @ts-ignore + // @ts-expect-error error.__formattedFrames.push(string); return ` at ${colors.stripColor(string)}`; }) .join("\n"); - // @ts-ignore + // @ts-expect-error Object.freeze(error.__callSiteEvals); - // @ts-ignore + // @ts-expect-error Object.freeze(error.__formattedFrames); return errorString; } diff --git a/cli/js/repl.ts b/cli/js/repl.ts index c84be68b1a973e..79273ed33844e5 100644 --- a/cli/js/repl.ts +++ b/cli/js/repl.ts @@ -35,7 +35,7 @@ function isRecoverableError(e: Error): boolean { // Returns `true` if `close()` is called in REPL. // We should quit the REPL when this function returns `true`. function isCloseCalled(): boolean { - // @ts-ignore + // @ts-expect-error return globalThis.closed; } diff --git a/cli/js/runtime_main.ts b/cli/js/runtime_main.ts index 3e81fc68013313..97205d205fa5ce 100644 --- a/cli/js/runtime_main.ts +++ b/cli/js/runtime_main.ts @@ -30,7 +30,7 @@ import { log, immutableDefine } from "./util.ts"; // TODO: factor out `Deno` global assignment to separate function // Add internal object to Deno object. // This is not exposed as part of the Deno types. -// @ts-ignore +// @ts-expect-error denoNs[internalSymbol] = internalObject; let windowIsClosing = false; @@ -71,7 +71,7 @@ export function bootstrapMainRuntime(): void { throw new Error("Worker runtime already bootstrapped"); } // Remove bootstrapping methods from global scope - // @ts-ignore + // @ts-expect-error globalThis.bootstrap = undefined; log("bootstrapMainRuntime"); hasBootstrapped = true; diff --git a/cli/js/runtime_worker.ts b/cli/js/runtime_worker.ts index ed735fd52a8258..4e92663a1bef07 100644 --- a/cli/js/runtime_worker.ts +++ b/cli/js/runtime_worker.ts @@ -33,7 +33,7 @@ import { setSignals } from "./signals.ts"; // TODO: factor out `Deno` global assignment to separate function // Add internal object to Deno object. // This is not exposed as part of the Deno types. -// @ts-ignore +// @ts-expect-error denoNs[internalSymbol] = internalObject; const encoder = new TextEncoder(); @@ -128,7 +128,7 @@ export function bootstrapWorkerRuntime( throw new Error("Worker runtime already bootstrapped"); } // Remove bootstrapping methods from global scope - // @ts-ignore + // @ts-expect-error globalThis.bootstrap = undefined; log("bootstrapWorkerRuntime"); hasBootstrapped = true; diff --git a/cli/js/testing.ts b/cli/js/testing.ts index 09acdc23d045ce..fc32fd604f7216 100644 --- a/cli/js/testing.ts +++ b/cli/js/testing.ts @@ -333,11 +333,10 @@ async function runTests({ const filterFn = createFilterFn(filter, skip); const testApi = new TestApi(TEST_REGISTRY, filterFn, failFast); - // @ts-ignore const originalConsole = globalThis.console; if (disableLog) { - // @ts-ignore + // @ts-expect-error globalThis.console = disabledConsole; } @@ -356,7 +355,6 @@ async function runTests({ } if (disableLog) { - // @ts-ignore globalThis.console = originalConsole; } diff --git a/cli/js/web/body.ts b/cli/js/web/body.ts index 9c4997755c967d..ffe3f0e595aedc 100644 --- a/cli/js/web/body.ts +++ b/cli/js/web/body.ts @@ -100,7 +100,6 @@ export class Body implements domTypes.Body { } if (this._bodySource instanceof ReadableStreamImpl) { - // @ts-ignore this._stream = this._bodySource; } if (typeof this._bodySource === "string") { @@ -290,7 +289,6 @@ export class Body implements domTypes.Body { enc.encode(this._bodySource).buffer as ArrayBuffer ); } else if (this._bodySource instanceof ReadableStreamImpl) { - // @ts-ignore return bufferFromStream(this._bodySource.getReader()); } else if (this._bodySource instanceof FormData) { const enc = new TextEncoder(); diff --git a/cli/js/web/console.ts b/cli/js/web/console.ts index 7a3f9241e4435f..69c9d3137a1c19 100644 --- a/cli/js/web/console.ts +++ b/cli/js/web/console.ts @@ -223,7 +223,7 @@ function groupEntries( let order = "padStart"; if (value !== undefined) { for (let i = 0; i < entries.length; i++) { - //@ts-ignore + //@ts-expect-error if (typeof value[i] !== "number" && typeof value[i] !== "bigint") { order = "padEnd"; break; @@ -239,7 +239,7 @@ function groupEntries( for (; j < max - 1; j++) { // In future, colors should be taken here into the account const padding = maxLineLength[j - i]; - //@ts-ignore + //@ts-expect-error str += `${entries[j]}, `[order](padding, " "); } if (order === "padStart") { @@ -412,7 +412,7 @@ function createMapString( }, group: false, }; - //@ts-ignore + //@ts-expect-error return createIterableString(value, ctx, level, maxLevel, printConfig); } @@ -494,7 +494,7 @@ function createRawObjectString( let baseString = ""; let shouldShowDisplayName = false; - // @ts-ignore + // @ts-expect-error let displayName = value[Symbol.toStringTag]; if (!displayName) { displayName = getClassInstanceName(value); @@ -515,7 +515,7 @@ function createRawObjectString( for (const key of symbolKeys) { entries.push( `${key.toString()}: ${stringifyWithQuotes( - // @ts-ignore + // @ts-expect-error value[key], ctx, level + 1, @@ -949,7 +949,7 @@ export class Console { name: "Trace", message, }; - // @ts-ignore + // @ts-expect-error Error.captureStackTrace(err, this.trace); this.error((err as Error).stack); }; diff --git a/cli/js/web/fetch.ts b/cli/js/web/fetch.ts index ec75d67cbe1e8d..a56ebe772e0491 100644 --- a/cli/js/web/fetch.ts +++ b/cli/js/web/fetch.ts @@ -282,7 +282,7 @@ export async function fetch( method = input.method; headers = input.headers; - //@ts-ignore + //@ts-expect-error if (input._bodySource) { body = new DataView(await input.arrayBuffer()); } diff --git a/cli/js/web/timers.ts b/cli/js/web/timers.ts index 90b6bba9494ef9..87b23de0675fac 100644 --- a/cli/js/web/timers.ts +++ b/cli/js/web/timers.ts @@ -239,7 +239,7 @@ export function setTimeout( ...args: Args ): number { checkBigInt(delay); - // @ts-ignore + // @ts-expect-error checkThis(this); return setTimer(cb, delay, args, false); } @@ -250,7 +250,7 @@ export function setInterval( ...args: Args ): number { checkBigInt(delay); - // @ts-ignore + // @ts-expect-error checkThis(this); return setTimer(cb, delay, args, true); } diff --git a/cli/tests/042_dyn_import_evalcontext.ts b/cli/tests/042_dyn_import_evalcontext.ts index 124a406d24de7b..e1cc485d147faa 100644 --- a/cli/tests/042_dyn_import_evalcontext.ts +++ b/cli/tests/042_dyn_import_evalcontext.ts @@ -1,4 +1,4 @@ -// @ts-ignore +// @ts-expect-error Deno.core.evalContext( "(async () => console.log(await import('./subdir/mod4.js')))()" ); diff --git a/cli/tests/unit/body_test.ts b/cli/tests/unit/body_test.ts index c8f783e0430eab..fd91b5ceddd2a3 100644 --- a/cli/tests/unit/body_test.ts +++ b/cli/tests/unit/body_test.ts @@ -42,7 +42,7 @@ unitTest( const body = buildBody(text); - // @ts-ignore + // @ts-expect-error body.contentType = "multipart/form-data;boundary=boundary"; const formData = await body.formData(); @@ -62,7 +62,7 @@ unitTest( const body = buildBody(text); - // @ts-ignore + // @ts-expect-error body.contentType = "application/x-www-form-urlencoded"; const formData = await body.formData(); diff --git a/cli/tests/unit/console_test.ts b/cli/tests/unit/console_test.ts index 02d71e4ca12443..35985dc1c23a48 100644 --- a/cli/tests/unit/console_test.ts +++ b/cli/tests/unit/console_test.ts @@ -22,7 +22,7 @@ const customInspect = Deno.customInspect; const { Console, stringifyArgs, - // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol + // @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol } = Deno[Deno.internal]; function stringify(...args: unknown[]): string { @@ -590,7 +590,7 @@ unitTest(function consoleTestStringifyIterable() { 0, <120 empty items> ]` ); -*/ + */ }); unitTest(async function consoleTestStringifyPromises(): Promise { @@ -727,7 +727,6 @@ unitTest(function consoleTestCallToStringOnLabel(): void { mockConsole((console) => { for (const method of methods) { let hasCalled = false; - // @ts-ignore console[method]({ toString(): void { hasCalled = true; diff --git a/cli/tests/unit/dispatch_json_test.ts b/cli/tests/unit/dispatch_json_test.ts index 4e95b86a2858f6..51c33befde219c 100644 --- a/cli/tests/unit/dispatch_json_test.ts +++ b/cli/tests/unit/dispatch_json_test.ts @@ -20,9 +20,9 @@ unitTest( ); unitTest(function malformedJsonControlBuffer(): void { - // @ts-ignore + // @ts-expect-error const opId = Deno.core.ops()["op_open"]; - // @ts-ignore + // @ts-expect-error const res = Deno.core.send(opId, new Uint8Array([1, 2, 3, 4, 5])); const resText = new TextDecoder().decode(res); // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/cli/tests/unit/dispatch_minimal_test.ts b/cli/tests/unit/dispatch_minimal_test.ts index afc17f4fb8434b..1c7ba11f0f99e0 100644 --- a/cli/tests/unit/dispatch_minimal_test.ts +++ b/cli/tests/unit/dispatch_minimal_test.ts @@ -26,9 +26,9 @@ unitTest(async function sendAsyncStackTrace(): Promise { }); unitTest(function malformedMinimalControlBuffer(): void { - // @ts-ignore + // @ts-expect-error const readOpId = Deno.core.ops()["op_read"]; - // @ts-ignore + // @ts-expect-error const res = Deno.core.send(readOpId, new Uint8Array([1, 2, 3, 4, 5])); const header = res.slice(0, 12); const buf32 = new Int32Array( diff --git a/cli/tests/unit/dom_iterable_test.ts b/cli/tests/unit/dom_iterable_test.ts index b9435b3bcc657e..c4e535a25759c5 100644 --- a/cli/tests/unit/dom_iterable_test.ts +++ b/cli/tests/unit/dom_iterable_test.ts @@ -20,7 +20,7 @@ function setup() { Base, // This is using an internal API we don't want published as types, so having // to cast to any to "trick" TypeScript - // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol + // @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol DomIterable: Deno[Deno.internal].DomIterableMixin(Base, dataSymbol), }; } diff --git a/cli/tests/unit/error_stack_test.ts b/cli/tests/unit/error_stack_test.ts index e5cedfcf51977a..eb0a5c0e6671f1 100644 --- a/cli/tests/unit/error_stack_test.ts +++ b/cli/tests/unit/error_stack_test.ts @@ -1,7 +1,7 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import { unitTest, assert } from "./test_util.ts"; -// @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol +// @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol const { setPrepareStackTrace } = Deno[Deno.internal]; interface CallSite { diff --git a/cli/tests/unit/event_target_test.ts b/cli/tests/unit/event_target_test.ts index 0c4eb4d0ddc386..cfbe5285b2d824 100644 --- a/cli/tests/unit/event_target_test.ts +++ b/cli/tests/unit/event_target_test.ts @@ -4,11 +4,8 @@ import { unitTest, assertEquals } from "./test_util.ts"; unitTest(function addEventListenerTest(): void { const document = new EventTarget(); - // @ts-ignore tests ignoring the type system for resilience assertEquals(document.addEventListener("x", null, false), undefined); - // @ts-ignore assertEquals(document.addEventListener("x", null, true), undefined); - // @ts-ignore assertEquals(document.addEventListener("x", null), undefined); }); @@ -71,11 +68,8 @@ unitTest(function anEventTargetCanBeSubclassed(): void { unitTest(function removingNullEventListenerShouldSucceed(): void { const document = new EventTarget(); - // @ts-ignore assertEquals(document.removeEventListener("x", null, false), undefined); - // @ts-ignore assertEquals(document.removeEventListener("x", null, true), undefined); - // @ts-ignore assertEquals(document.removeEventListener("x", null), undefined); }); diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts index ed17c869a3b13c..97b001b5525c36 100644 --- a/cli/tests/unit/fetch_test.ts +++ b/cli/tests/unit/fetch_test.ts @@ -93,7 +93,7 @@ unitTest({ perms: { net: true } }, async function fetchBodyUsed(): Promise< assertEquals(response.bodyUsed, false); assertThrows((): void => { // Assigning to read-only property throws in the strict mode. - // @ts-ignore + // @ts-expect-error response.bodyUsed = true; }); await response.blob(); @@ -595,7 +595,7 @@ unitTest({ perms: { net: true } }, async function fetchBodyReadTwice(): Promise< const methods = ["json", "text", "formData", "arrayBuffer"]; for (const method of methods) { try { - // @ts-ignore + // @ts-expect-error await response[method](); fail( "Reading body multiple times should failed, the stream should've been locked." diff --git a/cli/tests/unit/files_test.ts b/cli/tests/unit/files_test.ts index a035c70742803d..9ab74be89522e7 100644 --- a/cli/tests/unit/files_test.ts +++ b/cli/tests/unit/files_test.ts @@ -290,7 +290,7 @@ unitTest( // writing null should throw an error let err; try { - // @ts-ignore + // @ts-expect-error await file.write(null); } catch (e) { err = e; @@ -322,7 +322,7 @@ unitTest( // reading file into null buffer should throw an error let err; try { - // @ts-ignore + // @ts-expect-error await file.read(null); } catch (e) { err = e; diff --git a/cli/tests/unit/form_data_test.ts b/cli/tests/unit/form_data_test.ts index 10cbd30a79561c..5344d8512ad719 100644 --- a/cli/tests/unit/form_data_test.ts +++ b/cli/tests/unit/form_data_test.ts @@ -41,9 +41,9 @@ unitTest(function formDataParamsGetSuccess(): void { formData.append("a", "true"); formData.append("b", "false"); formData.append("a", "null"); - // @ts-ignore + // @ts-expect-error formData.append("d", undefined); - // @ts-ignore + // @ts-expect-error formData.append("e", null); assertEquals(formData.get("a"), "true"); assertEquals(formData.get("b"), "false"); @@ -70,10 +70,10 @@ unitTest(function formDataParamsSetSuccess(): void { assertEquals(formData.getAll("b"), ["false"]); formData.set("a", "false"); assertEquals(formData.getAll("a"), ["false"]); - // @ts-ignore + // @ts-expect-error formData.set("d", undefined); assertEquals(formData.get("d"), "undefined"); - // @ts-ignore + // @ts-expect-error formData.set("e", null); assertEquals(formData.get("e"), "null"); }); @@ -134,7 +134,7 @@ unitTest(function formDataParamsArgumentsCheck(): void { let hasThrown = 0; let errMsg = ""; try { - // @ts-ignore + // @ts-expect-error formData[method](); hasThrown = 1; } catch (err) { @@ -158,7 +158,7 @@ unitTest(function formDataParamsArgumentsCheck(): void { let errMsg = ""; try { - // @ts-ignore + // @ts-expect-error formData[method](); hasThrown = 1; } catch (err) { @@ -178,7 +178,7 @@ unitTest(function formDataParamsArgumentsCheck(): void { hasThrown = 0; errMsg = ""; try { - // @ts-ignore + // @ts-expect-error formData[method]("foo"); hasThrown = 1; } catch (err) { diff --git a/cli/tests/unit/globals_test.ts b/cli/tests/unit/globals_test.ts index aa8b4f46e7fe53..bb5e5c604b5d69 100644 --- a/cli/tests/unit/globals_test.ts +++ b/cli/tests/unit/globals_test.ts @@ -48,12 +48,12 @@ unitTest(function webAssemblyExists(): void { unitTest(function DenoNamespaceImmutable(): void { const denoCopy = window.Deno; try { - // @ts-ignore + // @ts-expect-error Deno = 1; } catch {} assert(denoCopy === Deno); try { - // @ts-ignore + // @ts-expect-error window.Deno = 1; } catch {} assert(denoCopy === Deno); @@ -64,7 +64,7 @@ unitTest(function DenoNamespaceImmutable(): void { const { readFile } = Deno; try { - // @ts-ignore + // @ts-expect-error Deno.readFile = 1; } catch {} assert(readFile === Deno.readFile); @@ -73,19 +73,19 @@ unitTest(function DenoNamespaceImmutable(): void { } catch {} assert(readFile === Deno.readFile); - // @ts-ignore + // @ts-expect-error const { print } = Deno.core; try { - // @ts-ignore + // @ts-expect-error Deno.core.print = 1; } catch {} - // @ts-ignore + // @ts-expect-error assert(print === Deno.core.print); try { - // @ts-ignore + // @ts-expect-error delete Deno.core.print; } catch {} - // @ts-ignore + // @ts-expect-error assert(print === Deno.core.print); }); diff --git a/cli/tests/unit/headers_test.ts b/cli/tests/unit/headers_test.ts index aaa8298379dcb8..6dd60c8c560b47 100644 --- a/cli/tests/unit/headers_test.ts +++ b/cli/tests/unit/headers_test.ts @@ -7,7 +7,7 @@ import { } from "./test_util.ts"; const { stringifyArgs, - // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol + // @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol } = Deno[Deno.internal]; // Logic heavily copied from web-platform-tests, make @@ -18,7 +18,7 @@ unitTest(function newHeaderTest(): void { new Headers(undefined); new Headers({}); try { - // @ts-ignore + // @ts-expect-error new Headers(null); } catch (e) { assertEquals( @@ -32,7 +32,7 @@ const headerDict: Record = { name1: "value1", name2: "value2", name3: "value3", - // @ts-ignore + // @ts-expect-error name4: undefined, "Content-Type": "value4", }; @@ -269,7 +269,7 @@ unitTest(function headerParamsArgumentsCheck(): void { let hasThrown = 0; let errMsg = ""; try { - // @ts-ignore + // @ts-expect-error headers[method](); hasThrown = 1; } catch (err) { @@ -293,7 +293,7 @@ unitTest(function headerParamsArgumentsCheck(): void { let errMsg = ""; try { - // @ts-ignore + // @ts-expect-error headers[method](); hasThrown = 1; } catch (err) { @@ -313,7 +313,7 @@ unitTest(function headerParamsArgumentsCheck(): void { hasThrown = 0; errMsg = ""; try { - // @ts-ignore + // @ts-expect-error headers[method]("foo"); hasThrown = 1; } catch (err) { diff --git a/cli/tests/unit/internals_test.ts b/cli/tests/unit/internals_test.ts index abd4c94c3d4945..3f4bdae79e51bc 100644 --- a/cli/tests/unit/internals_test.ts +++ b/cli/tests/unit/internals_test.ts @@ -4,7 +4,7 @@ import { unitTest, assert } from "./test_util.ts"; unitTest(function internalsExists(): void { const { stringifyArgs, - // @ts-ignore TypeScript (as of 3.7) does not support indexing namespaces by symbol + // @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol } = Deno[Deno.internal]; assert(!!stringifyArgs); }); diff --git a/cli/tests/unit/request_test.ts b/cli/tests/unit/request_test.ts index 8a276c5e79284b..be6e956b771fd5 100644 --- a/cli/tests/unit/request_test.ts +++ b/cli/tests/unit/request_test.ts @@ -10,7 +10,7 @@ unitTest(function fromInit(): void { }, }); - // @ts-ignore + // @ts-expect-error assertEquals("ahoyhoy", req._bodySource); assertEquals(req.url, "https://example.com"); assertEquals(req.headers.get("test-header"), "value"); @@ -18,13 +18,13 @@ unitTest(function fromInit(): void { unitTest(function fromRequest(): void { const r = new Request("https://example.com"); - // @ts-ignore + // @ts-expect-error r._bodySource = "ahoyhoy"; r.headers.set("test-header", "value"); const req = new Request(r); - // @ts-ignore + // @ts-expect-error assertEquals(req._bodySource, r._bodySource); assertEquals(req.url, r.url); assertEquals(req.headers.get("test-header"), r.headers.get("test-header")); @@ -44,6 +44,6 @@ unitTest(async function cloneRequestBodyStream(): Promise { assertEquals(b1, b2); - // @ts-ignore + // @ts-expect-error assert(r1._bodySource !== r2._bodySource); }); diff --git a/cli/tests/unit/streams_internal_test.ts b/cli/tests/unit/streams_internal_test.ts index f324da194500e0..f49c9f494edd8b 100644 --- a/cli/tests/unit/streams_internal_test.ts +++ b/cli/tests/unit/streams_internal_test.ts @@ -8,7 +8,7 @@ unitTest(function streamReadableHwmError() { () => { new ReadableStream( undefined, - // @ts-ignore + // @ts-expect-error { highWaterMark } ); }, @@ -20,7 +20,7 @@ unitTest(function streamReadableHwmError() { assertThrows(() => { new ReadableStream( undefined, - // @ts-ignore + // @ts-expect-error { highWaterMark: Symbol("hwk") } ); }, TypeError); @@ -33,7 +33,7 @@ unitTest(function streamWriteableHwmError() { () => { new WritableStream( undefined, - // @ts-ignore + // @ts-expect-error new CountQueuingStrategy({ highWaterMark }) ); }, @@ -45,7 +45,7 @@ unitTest(function streamWriteableHwmError() { assertThrows(() => { new WritableStream( undefined, - // @ts-ignore + // @ts-expect-error new CountQueuingStrategy({ highWaterMark: Symbol("hwmk") }) ); }, TypeError); @@ -59,7 +59,7 @@ unitTest(function streamTransformHwmError() { new TransformStream( undefined, undefined, - // @ts-ignore + // @ts-expect-error { highWaterMark } ); }, @@ -72,7 +72,7 @@ unitTest(function streamTransformHwmError() { new TransformStream( undefined, undefined, - // @ts-ignore + // @ts-expect-error { highWaterMark: Symbol("hwmk") } ); }, TypeError); diff --git a/cli/tests/unit/unit_test_runner.ts b/cli/tests/unit/unit_test_runner.ts index 715dda50010654..e3df358d7cf3cb 100755 --- a/cli/tests/unit/unit_test_runner.ts +++ b/cli/tests/unit/unit_test_runner.ts @@ -11,7 +11,7 @@ import { reportToConn, } from "./test_util.ts"; -// @ts-ignore +// @ts-expect-error const internalObj = Deno[Deno.internal]; // eslint-disable-next-line @typescript-eslint/no-explicit-any const reportToConsole = internalObj.reportToConsole as (message: any) => void; diff --git a/cli/tests/unit/url_search_params_test.ts b/cli/tests/unit/url_search_params_test.ts index 7b7dbab764059a..ce55a752057b6d 100644 --- a/cli/tests/unit/url_search_params_test.ts +++ b/cli/tests/unit/url_search_params_test.ts @@ -177,7 +177,7 @@ unitTest(function urlSearchParamsAppendArgumentsCheck(): void { const searchParams = new URLSearchParams(); let hasThrown = 0; try { - // @ts-ignore + // @ts-expect-error searchParams[method](); hasThrown = 1; } catch (err) { @@ -194,7 +194,7 @@ unitTest(function urlSearchParamsAppendArgumentsCheck(): void { const searchParams = new URLSearchParams(); let hasThrown = 0; try { - // @ts-ignore + // @ts-expect-error searchParams[method]("foo"); hasThrown = 1; } catch (err) { @@ -235,7 +235,7 @@ unitTest(function urlSearchParamsCustomSymbolIterator(): void { unitTest( function urlSearchParamsCustomSymbolIteratorWithNonStringParams(): void { const params = {}; - // @ts-ignore + // @ts-expect-error params[Symbol.iterator] = function* (): IterableIterator<[number, number]> { yield [1, 2]; }; diff --git a/std/http/server_test.ts b/std/http/server_test.ts index 0560f7f8daab56..0423b872626ebb 100644 --- a/std/http/server_test.ts +++ b/std/http/server_test.ts @@ -496,7 +496,6 @@ test({ async fn(): Promise { const serverRoutine = async (): Promise => { const server = serve(":8124"); - // @ts-ignore for await (const req of server) { await assertThrowsAsync(async () => { await req.respond({ diff --git a/std/mime/multipart_test.ts b/std/mime/multipart_test.ts index 61afdba03635bf..b7c0cb969c5dcc 100644 --- a/std/mime/multipart_test.ts +++ b/std/mime/multipart_test.ts @@ -145,7 +145,7 @@ test("multipartMultipartWriter3", async function (): Promise { ); await assertThrowsAsync( async (): Promise => { - // @ts-ignore + // @ts-expect-error await mw.writeFile("bar", "file", null); }, Error, diff --git a/std/node/global.ts b/std/node/global.ts index c877d1d531b3c3..7d147cc4b1de8d 100644 --- a/std/node/global.ts +++ b/std/node/global.ts @@ -5,5 +5,5 @@ Object.defineProperty(globalThis, Symbol.toStringTag, { configurable: true, }); -// @ts-ignore +// @ts-expect-error globalThis["global"] = globalThis; diff --git a/std/node/module.ts b/std/node/module.ts index 4e0c55c1d84a6f..71c25beacaf8de 100644 --- a/std/node/module.ts +++ b/std/node/module.ts @@ -263,9 +263,9 @@ class Module { message = message + "\nRequire stack:\n- " + requireStack.join("\n- "); } const err = new Error(message); - // @ts-ignore + // @ts-expect-error err.code = "MODULE_NOT_FOUND"; - // @ts-ignore + // @ts-expect-error err.requireStack = requireStack; throw err; } @@ -737,7 +737,7 @@ function tryPackage( `Cannot find module '${filename}'. ` + 'Please verify that the package.json has a valid "main" entry' ); - // @ts-ignore + // @ts-expect-error err.code = "MODULE_NOT_FOUND"; throw err; } @@ -882,7 +882,7 @@ function applyExports(basePath: string, expansion: string): string { `Package exports for '${basePath}' do not define ` + `a '${mappingKey}' subpath` ); - // @ts-ignore + // @ts-expect-error e.code = "MODULE_NOT_FOUND"; throw e; } @@ -982,7 +982,7 @@ function resolveExportsTarget( } else { e = new Error(`No valid exports main found for '${basePath}'`); } - // @ts-ignore + // @ts-expect-error e.code = "MODULE_NOT_FOUND"; throw e; } @@ -1007,7 +1007,7 @@ const CircularRequirePrototypeWarningProxy = new Proxy( { // eslint-disable-next-line @typescript-eslint/no-explicit-any get(target, prop): any { - // @ts-ignore + // @ts-expect-error if (prop in target) return target[prop]; emitCircularRequireWarning(prop); return undefined; @@ -1058,7 +1058,7 @@ type RequireWrapper = ( function wrapSafe(filename: string, content: string): RequireWrapper { // TODO: fix this const wrapper = Module.wrap(content); - // @ts-ignore + // @ts-expect-error const [f, err] = Deno.core.evalContext(wrapper, filename); if (err) { throw err; diff --git a/std/signal/test.ts b/std/signal/test.ts index 15ebbdcc723c36..13c2998db1f8b8 100644 --- a/std/signal/test.ts +++ b/std/signal/test.ts @@ -9,7 +9,7 @@ test({ fn() { assertThrows( () => { - // @ts-ignore + // @ts-expect-error signal(); }, Error, diff --git a/std/uuid/tests/isNil.ts b/std/uuid/tests/isNil.ts index 4514576daa7606..1f0db416e2bfac 100644 --- a/std/uuid/tests/isNil.ts +++ b/std/uuid/tests/isNil.ts @@ -1,7 +1,6 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import { assert } from "../../testing/asserts.ts"; const { test } = Deno; -// @ts-ignore import { NIL_UUID, isNil } from "../mod.ts"; test({ diff --git a/std/ws/mod.ts b/std/ws/mod.ts index 324588af01db5d..97c77baab024d8 100644 --- a/std/ws/mod.ts +++ b/std/ws/mod.ts @@ -491,7 +491,7 @@ export async function handshake( throw new Error("ws: invalid status line: " + statusLine); } - // @ts-ignore + // @ts-expect-error const { version, statusCode } = m.groups; if (version !== "HTTP/1.1" || statusCode !== "101") { throw new Error( From 44477596eda3ca50ea6597d4af1fd809a01e2bdc Mon Sep 17 00:00:00 2001 From: Robin Wieruch Date: Tue, 26 May 2020 16:08:23 +0200 Subject: [PATCH 24/30] improve docs (#5872) --- docs/examples/tcp_echo.md | 4 ++-- docs/getting_started.md | 2 +- docs/getting_started/first_steps.md | 13 ++++++------- docs/runtime/compiler_apis.md | 2 +- docs/tools/debugger.md | 8 ++++---- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/docs/examples/tcp_echo.md b/docs/examples/tcp_echo.md index 360c5faccd58ea..5dfc71b8317bba 100644 --- a/docs/examples/tcp_echo.md +++ b/docs/examples/tcp_echo.md @@ -1,7 +1,7 @@ ## TCP echo server -This is an example of a simple server which accepts connections on port 8080, -and returns to the client anything it sends. +This is an example of a server which accepts connections on port 8080, and +returns to the client anything it sends. ```ts const listener = Deno.listen({ port: 8080 }); diff --git a/docs/getting_started.md b/docs/getting_started.md index 7737f08d746609..b2b0eb8639688d 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -4,7 +4,7 @@ In this chapter we'll discuss: - Installing Deno - Setting up your environment -- Running a simple `Hello World` script +- Running a `Hello World` script - Writing our own script - Understanding permissions - Using Deno with TypeScript diff --git a/docs/getting_started/first_steps.md b/docs/getting_started/first_steps.md index 9186af7302ff41..4a551c41566f1e 100644 --- a/docs/getting_started/first_steps.md +++ b/docs/getting_started/first_steps.md @@ -1,7 +1,6 @@ ## First steps -This page contains some simple examples to teach you about the fundamentals of -Deno. +This page contains some examples to teach you about the fundamentals of Deno. This document assumes that you have some prior knowledge of JavaScript, especially about `async`/`await`. If you have no prior knowledge of JavaScript, @@ -14,8 +13,8 @@ before attempting to start with Deno. Deno is a runtime for JavaScript/TypeScript which tries to be web compatible and use modern features wherever possible. -Browser compatibility means a simple `Hello World` program in Deno is the same -as the one you can run in the browser: +Browser compatibility means a `Hello World` program in Deno is the same as the +one you can run in the browser: ```ts console.log("Welcome to Deno 🦕"); @@ -106,10 +105,10 @@ Try the program: deno run --allow-read https://deno.land/std/examples/cat.ts /etc/passwd ``` -### A simple TCP server +### TCP server -This is an example of a simple server which accepts connections on port 8080, -and returns to the client anything it sends. +This is an example of a server which accepts connections on port 8080, and +returns to the client anything it sends. ```ts const hostname = "0.0.0.0"; diff --git a/docs/runtime/compiler_apis.md b/docs/runtime/compiler_apis.md index ebb14f6b3cb208..c2d2348360dde5 100644 --- a/docs/runtime/compiler_apis.md +++ b/docs/runtime/compiler_apis.md @@ -52,7 +52,7 @@ const [diagnostics, emitMap] = await Deno.compile( ); ``` -In this case `emitMap` will contain a simple `console.log()` statement. +In this case `emitMap` will contain a `console.log()` statement. ### `Deno.bundle()` diff --git a/docs/tools/debugger.md b/docs/tools/debugger.md index e9eccb6f02ac1d..5725d029a2636c 100644 --- a/docs/tools/debugger.md +++ b/docs/tools/debugger.md @@ -14,9 +14,9 @@ first line of code. ### Chrome Devtools -Let's try debugging simple program using Chrome Devtools; for this purpose we'll -use [file_server.ts](https://deno.land/std@v0.50.0/http/file_server.ts) from -`std`; a simple static file server. +Let's try debugging a program using Chrome Devtools; for this purpose we'll use +[file_server.ts](https://deno.land/std@v0.50.0/http/file_server.ts) from `std`; +a static file server. Use `--inspect-brk` flag to break execution on the first line. @@ -83,7 +83,7 @@ Deno can be debugged using VSCode. Official support in plugin is being worked on - https://github.com/denoland/vscode_deno/issues/12 -We can still attach debugger by manually providing simple `launch.json` config: +We can still attach debugger by manually providing a `launch.json` config: ```json { From 845bc443da6026903aefa7a6556ecc7c0de371fe Mon Sep 17 00:00:00 2001 From: Robin Wieruch Date: Tue, 26 May 2020 16:09:47 +0200 Subject: [PATCH 25/30] improve docs (#5873) --- docs/tools/debugger.md | 4 ++-- std/http/README.md | 4 ++-- std/http/server.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/tools/debugger.md b/docs/tools/debugger.md index 5725d029a2636c..8bd4990382c132 100644 --- a/docs/tools/debugger.md +++ b/docs/tools/debugger.md @@ -108,10 +108,10 @@ This time let's try with local source file, create `server.ts`: ```ts import { serve } from "https://deno.land/std@v0.50.0/http/server.ts"; -const s = serve({ port: 8000 }); +const server = serve({ port: 8000 }); console.log("http://localhost:8000/"); -for await (const req of s) { +for await (const req of server) { req.respond({ body: "Hello World\n" }); } ``` diff --git a/std/http/README.md b/std/http/README.md index 4e30a3a56542a3..0f97fd0c0e88b6 100644 --- a/std/http/README.md +++ b/std/http/README.md @@ -2,9 +2,9 @@ ```typescript import { serve } from "https://deno.land/std/http/server.ts"; -const s = serve({ port: 8000 }); +const server = serve({ port: 8000 }); console.log("http://localhost:8000/"); -for await (const req of s) { +for await (const req of server) { req.respond({ body: "Hello World\n" }); } ``` diff --git a/std/http/server.ts b/std/http/server.ts index a372b39a50c255..787820a563bd74 100644 --- a/std/http/server.ts +++ b/std/http/server.ts @@ -247,8 +247,8 @@ export type HTTPOptions = Omit; * * import { serve } from "https://deno.land/std/http/server.ts"; * const body = "Hello World\n"; - * const s = serve({ port: 8000 }); - * for await (const req of s) { + * const server = serve({ port: 8000 }); + * for await (const req of server) { * req.respond({ body }); * } */ From d4711feba86ce26044ebffd625b9b9ee872e0f1a Mon Sep 17 00:00:00 2001 From: crowlKats <13135287+crowlKats@users.noreply.github.com> Date: Tue, 26 May 2020 19:11:08 +0200 Subject: [PATCH 26/30] docs: update JetBrains environment support (#5877) --- docs/getting_started/setup_your_environment.md | 10 +++++----- docs/tools/debugger.md | 8 ++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/getting_started/setup_your_environment.md b/docs/getting_started/setup_your_environment.md index 70bcb582112bcf..2dfeee78c3714e 100644 --- a/docs/getting_started/setup_your_environment.md +++ b/docs/getting_started/setup_your_environment.md @@ -54,12 +54,12 @@ Please report any issues. #### JetBrains IDEs -Support for JetBrains IDEs is not yet available, but you can follow and upvote -these issues to stay up to date: +Support for JetBrains IDEs is available through +[the Deno plugin](https://plugins.jetbrains.com/plugin/14382-deno). -- https://youtrack.jetbrains.com/issue/WEB-41607 -- https://youtrack.jetbrains.com/issue/WEB-42983 -- https://youtrack.jetbrains.com/issue/WEB-31667 +For more information on how to set-up your JetBrains IDE for Deno, read +[this comment](https://youtrack.jetbrains.com/issue/WEB-41607#focus=streamItem-27-4160152.0-0) +on YouTrack. #### Vim and NeoVim diff --git a/docs/tools/debugger.md b/docs/tools/debugger.md index 8bd4990382c132..a6aed4711ebe2a 100644 --- a/docs/tools/debugger.md +++ b/docs/tools/debugger.md @@ -122,6 +122,14 @@ Change `` to `server.ts` and run created configuration: ![VSCode debugger](../images/debugger7.jpg) +### JetBrains IDEs + +You can debug Deno using your JetBrains IDE by right-clicking the file you want +to debug and selecting the `Debug 'Deno: '` option. This will create +a run/debug configuration which has no permission flags set, so to change that +you need to modify the run/debug configuration and change the `Arguments` field +with the required flags. + ### Other Any client that implements Devtools protocol should be able to connect to Deno From f7b4523178b01a7e485e98443f03a6973df3e376 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Tue, 26 May 2020 15:28:48 -0400 Subject: [PATCH 27/30] Add sponsor button (#5880) --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000000000..17743fbd00d7a1 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: denoland From 27708fe87316d11b8d99459db257d381ee049430 Mon Sep 17 00:00:00 2001 From: Chris Knight Date: Wed, 27 May 2020 03:12:02 +0100 Subject: [PATCH 28/30] doc: various runtime doc updates (#5885) --- docs/runtime.md | 5 ++++- docs/runtime/program_lifecycle.md | 12 ++++++++++-- docs/runtime/stability.md | 21 ++++++++++++++++----- docs/runtime/workers.md | 29 ++++++++++++++++++++++------- 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/docs/runtime.md b/docs/runtime.md index 735ff328e99154..50cca6def8adb6 100644 --- a/docs/runtime.md +++ b/docs/runtime.md @@ -9,8 +9,11 @@ on For APIs where a web standard already exists, like `fetch` for HTTP requests, Deno uses these rather than inventing a new proprietary API. -The documentation for all of these Web APIs can be found on +The detailed documentation for implemented Web APIs can be found on [doc.deno.land](https://doc.deno.land/https/raw.githubusercontent.com/denoland/deno/master/cli/js/lib.deno.shared_globals.d.ts). +Additionally, a full list of the Web APIs which Deno implements is also +available +[in the repository](https://github.com/denoland/deno/blob/master/cli/js/web/README.md). The TypeScript definitions for the implemented web APIs can be found in the [`lib.deno.shared_globals.d.ts`](https://github.com/denoland/deno/blob/master/cli/js/lib.deno.shared_globals.d.ts) diff --git a/docs/runtime/program_lifecycle.md b/docs/runtime/program_lifecycle.md index 4a94d6724bbada..7f60350485facf 100644 --- a/docs/runtime/program_lifecycle.md +++ b/docs/runtime/program_lifecycle.md @@ -8,8 +8,9 @@ for `unload` events need to be synchronous. Both events cannot be cancelled. Example: +**main.ts** + ```ts -// main.ts import "./imported.ts"; const handler = (e: Event): void => { @@ -29,8 +30,11 @@ window.onunload = (e: Event): void => { }; console.log("log from main script"); +``` + +**imported.ts** -// imported.ts +```ts const handler = (e: Event): void => { console.log(`got ${e.type} event in event handler (imported)`); }; @@ -68,3 +72,7 @@ got unload event in event handler (main) All listeners added using `window.addEventListener` were run, but `window.onload` and `window.onunload` defined in `main.ts` overrode handlers defined in `imported.ts`. + +In other words, you can register multiple `window.addEventListener` `"load"` or +`"unload"` events, but only the last loaded `window.onload` or `window.onunload` +events will be executed. diff --git a/docs/runtime/stability.md b/docs/runtime/stability.md index 7fcc0f0b0d5526..298a788d4605c0 100644 --- a/docs/runtime/stability.md +++ b/docs/runtime/stability.md @@ -5,17 +5,28 @@ strive to make code working under 1.0.0 continue to work in future versions. However, not all of Deno's features are ready for production yet. Features which are not ready, because they are still in draft phase, are locked behind the -`--unstable` command line flag. Passing this flag does a few things: +`--unstable` command line flag. + +```shell +deno run --unstable mod_which_uses_unstable_stuff.ts +``` + +Passing this flag does a few things: - It enables the use of unstable APIs during runtime. - It adds the - [`lib.deno.unstable.d.ts`](https://github.com/denoland/deno/blob/master/cli/js/lib.deno.unstable.d.ts) + [`lib.deno.unstable.d.ts`](https://doc.deno.land/https/raw.githubusercontent.com/denoland/deno/master/cli/js/lib.deno.unstable.d.ts) file to the list of TypeScript definitions that are used for type checking. This includes the output of `deno types`. -You should be aware that unstable APIs have probably **not undergone a security +You should be aware that many unstable APIs have **not undergone a security review**, are likely to have **breaking API changes** in the future, and are **not ready for production**. -Furthermore Deno's standard modules (https://deno.land/std/) are not yet stable. -We version the standard modules differently from the CLI to reflect this. +### Standard modules + +Deno's standard modules (https://deno.land/std/) are not yet stable. We +currently version the standard modules differently from the CLI to reflect this. +Note that unlike the `Deno` namespace, the use of the standard modules do not +require the `--unstable` flag (unless the standard module itself makes use of an +unstable Deno feature). diff --git a/docs/runtime/workers.md b/docs/runtime/workers.md index 89541cee75dbe2..110255b8b63bc2 100644 --- a/docs/runtime/workers.md +++ b/docs/runtime/workers.md @@ -25,11 +25,15 @@ requires appropriate permission for this action. For workers using local modules; `--allow-read` permission is required: +**main.ts** + ```ts -// main.ts new Worker("./worker.ts", { type: "module" }); +``` -// worker.ts +**worker.ts** + +```ts console.log("hello world"); self.close(); ``` @@ -44,11 +48,15 @@ hello world For workers using remote modules; `--allow-net` permission is required: +**main.ts** + ```ts -// main.ts new Worker("https://example.com/worker.ts", { type: "module" }); +``` -// worker.ts +**worker.ts** (at https[]()://example.com/worker.ts) + +```ts console.log("hello world"); self.close(); ``` @@ -70,20 +78,27 @@ By default the `Deno` namespace is not available in worker scope. To add the `Deno` namespace pass `deno: true` option when creating new worker: +**main.js** + ```ts -// main.js const worker = new Worker("./worker.js", { type: "module", deno: true }); worker.postMessage({ filename: "./log.txt" }); +``` -// worker.js +**worker.js** + +```ts self.onmessage = async (e) => { const { filename } = e.data; const text = await Deno.readTextFile(filename); console.log(text); self.close(); }; +``` + +**log.txt** -// log.txt +``` hello world ``` From 304d78f2c97763a0943526be3d5587a2706e7a81 Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Wed, 27 May 2020 12:04:38 +0900 Subject: [PATCH 29/30] Bump actions/checkout from v1 to v2 (#5882) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 902dad72b836ac..f5c47006dd6d39 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: run: git config --global core.symlinks true - name: Clone repository - uses: actions/checkout@v1 + uses: actions/checkout@v2 with: # Use depth > 1, because sometimes we need to rebuild master and if # other commits have landed it will become impossible to rebuild if From 23bbb3945496ad5b8378ccd427f6d017d1cc7c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BF=B7=E6=B8=A1?= Date: Wed, 27 May 2020 13:06:08 +0800 Subject: [PATCH 30/30] doc: official denoland extension (#5890) --- docs/getting_started/setup_your_environment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting_started/setup_your_environment.md b/docs/getting_started/setup_your_environment.md index 2dfeee78c3714e..f916331bcd7951 100644 --- a/docs/getting_started/setup_your_environment.md +++ b/docs/getting_started/setup_your_environment.md @@ -49,7 +49,7 @@ The community has developed extensions for some editors to solve these issues: The beta version of [vscode_deno](https://github.com/denoland/vscode_deno) is published on the -[Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=justjavac.vscode-deno). +[Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno). Please report any issues. #### JetBrains IDEs