mirror of
https://github.com/denoland/deno.git
synced 2024-11-22 15:06:54 -05:00
Port internal TS code to JS (#6793)
Co-authored-by: Ryan Dahl <ry@tinyclouds.org>
This commit is contained in:
parent
53adde866d
commit
fa61956f03
204 changed files with 16611 additions and 19384 deletions
152
cli/build.rs
152
cli/build.rs
|
@ -1,9 +1,11 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
use deno_core::include_crate_modules;
|
||||
|
||||
use deno_core::js_check;
|
||||
use deno_core::CoreIsolate;
|
||||
use deno_core::StartupData;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
|
@ -11,6 +13,67 @@ extern crate winapi;
|
|||
#[cfg(target_os = "windows")]
|
||||
extern crate winres;
|
||||
|
||||
fn create_snapshot(
|
||||
mut isolate: CoreIsolate,
|
||||
snapshot_path: &Path,
|
||||
files: Vec<String>,
|
||||
) {
|
||||
for file in files {
|
||||
println!("cargo:rerun-if-changed={}", file);
|
||||
js_check(isolate.execute(&file, &std::fs::read_to_string(&file).unwrap()));
|
||||
}
|
||||
|
||||
let snapshot = isolate.snapshot();
|
||||
let snapshot_slice: &[u8] = &*snapshot;
|
||||
println!("Snapshot size: {}", snapshot_slice.len());
|
||||
std::fs::write(&snapshot_path, snapshot_slice).unwrap();
|
||||
println!("Snapshot written to: {} ", snapshot_path.display());
|
||||
}
|
||||
|
||||
fn create_runtime_snapshot(snapshot_path: &Path, files: Vec<String>) {
|
||||
let runtime_isolate = CoreIsolate::new(StartupData::None, true);
|
||||
create_snapshot(runtime_isolate, snapshot_path, files);
|
||||
}
|
||||
|
||||
fn create_compiler_snapshot(
|
||||
snapshot_path: &Path,
|
||||
files: Vec<String>,
|
||||
cwd: &Path,
|
||||
) {
|
||||
let mut runtime_isolate = CoreIsolate::new(StartupData::None, true);
|
||||
let mut custom_libs: HashMap<String, PathBuf> = HashMap::new();
|
||||
custom_libs.insert(
|
||||
"lib.deno.window.d.ts".to_string(),
|
||||
cwd.join("js2/lib.deno.window.d.ts"),
|
||||
);
|
||||
custom_libs.insert(
|
||||
"lib.deno.worker.d.ts".to_string(),
|
||||
cwd.join("js2/lib.deno.worker.d.ts"),
|
||||
);
|
||||
custom_libs.insert(
|
||||
"lib.deno.shared_globals.d.ts".to_string(),
|
||||
cwd.join("js2/lib.deno.shared_globals.d.ts"),
|
||||
);
|
||||
custom_libs.insert(
|
||||
"lib.deno.ns.d.ts".to_string(),
|
||||
cwd.join("js2/lib.deno.ns.d.ts"),
|
||||
);
|
||||
custom_libs.insert(
|
||||
"lib.deno.unstable.d.ts".to_string(),
|
||||
cwd.join("js2/lib.deno.unstable.d.ts"),
|
||||
);
|
||||
runtime_isolate.register_op(
|
||||
"op_fetch_asset",
|
||||
deno_typescript::op_fetch_asset(custom_libs),
|
||||
);
|
||||
|
||||
js_check(
|
||||
runtime_isolate.execute("typescript.js", deno_typescript::TYPESCRIPT_CODE),
|
||||
);
|
||||
|
||||
create_snapshot(runtime_isolate, snapshot_path, files);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Don't build V8 if "cargo doc" is being run. This is to support docs.rs.
|
||||
if env::var_os("RUSTDOCFLAGS").is_some() {
|
||||
|
@ -30,82 +93,31 @@ fn main() {
|
|||
std::env::var("TARGET").unwrap()
|
||||
);
|
||||
|
||||
let extern_crate_modules = include_crate_modules![deno_core];
|
||||
|
||||
let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
||||
let o = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
|
||||
// Main snapshot
|
||||
let root_names = vec![c.join("js/main.ts")];
|
||||
let bundle_path = o.join("CLI_SNAPSHOT.js");
|
||||
let snapshot_path = o.join("CLI_SNAPSHOT.bin");
|
||||
let runtime_snapshot_path = o.join("CLI_SNAPSHOT.bin");
|
||||
let compiler_snapshot_path = o.join("COMPILER_SNAPSHOT.bin");
|
||||
|
||||
let main_module_name = deno_typescript::compile_bundle(
|
||||
&bundle_path,
|
||||
root_names,
|
||||
Some(extern_crate_modules.clone()),
|
||||
)
|
||||
.expect("Bundle compilation failed");
|
||||
assert!(bundle_path.exists());
|
||||
let mut js_files = std::fs::read_dir("js2/")
|
||||
.unwrap()
|
||||
.map(|dir_entry| {
|
||||
let file = dir_entry.unwrap();
|
||||
file.path().to_string_lossy().to_string()
|
||||
})
|
||||
.filter(|filename| filename.ends_with(".js"))
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let mut runtime_isolate = CoreIsolate::new(StartupData::None, true);
|
||||
js_files.sort();
|
||||
|
||||
deno_typescript::mksnapshot_bundle(
|
||||
&mut runtime_isolate,
|
||||
&snapshot_path,
|
||||
&bundle_path,
|
||||
&main_module_name,
|
||||
)
|
||||
.expect("Failed to create snapshot");
|
||||
|
||||
// Compiler snapshot
|
||||
let root_names = vec![c.join("js/compiler.ts")];
|
||||
let bundle_path = o.join("COMPILER_SNAPSHOT.js");
|
||||
let snapshot_path = o.join("COMPILER_SNAPSHOT.bin");
|
||||
|
||||
let main_module_name = deno_typescript::compile_bundle(
|
||||
&bundle_path,
|
||||
root_names,
|
||||
Some(extern_crate_modules),
|
||||
)
|
||||
.expect("Bundle compilation failed");
|
||||
assert!(bundle_path.exists());
|
||||
|
||||
let mut runtime_isolate = CoreIsolate::new(StartupData::None, true);
|
||||
|
||||
let mut custom_libs: HashMap<String, PathBuf> = HashMap::new();
|
||||
custom_libs.insert(
|
||||
"lib.deno.window.d.ts".to_string(),
|
||||
c.join("js/lib.deno.window.d.ts"),
|
||||
);
|
||||
custom_libs.insert(
|
||||
"lib.deno.worker.d.ts".to_string(),
|
||||
c.join("js/lib.deno.worker.d.ts"),
|
||||
);
|
||||
custom_libs.insert(
|
||||
"lib.deno.shared_globals.d.ts".to_string(),
|
||||
c.join("js/lib.deno.shared_globals.d.ts"),
|
||||
);
|
||||
custom_libs.insert(
|
||||
"lib.deno.ns.d.ts".to_string(),
|
||||
c.join("js/lib.deno.ns.d.ts"),
|
||||
);
|
||||
custom_libs.insert(
|
||||
"lib.deno.unstable.d.ts".to_string(),
|
||||
c.join("js/lib.deno.unstable.d.ts"),
|
||||
);
|
||||
runtime_isolate.register_op(
|
||||
"op_fetch_asset",
|
||||
deno_typescript::op_fetch_asset(custom_libs),
|
||||
);
|
||||
|
||||
deno_typescript::mksnapshot_bundle_ts(
|
||||
&mut runtime_isolate,
|
||||
&snapshot_path,
|
||||
&bundle_path,
|
||||
&main_module_name,
|
||||
)
|
||||
.expect("Failed to create snapshot");
|
||||
let runtime_files = js_files
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|filepath| !filepath.ends_with("compiler.js"))
|
||||
.collect::<Vec<String>>();
|
||||
create_runtime_snapshot(&runtime_snapshot_path, runtime_files);
|
||||
create_compiler_snapshot(&compiler_snapshot_path, js_files, &c);
|
||||
|
||||
set_binary_metadata();
|
||||
}
|
||||
|
|
|
@ -1461,8 +1461,8 @@ mod tests {
|
|||
.await;
|
||||
assert!(r.is_err());
|
||||
|
||||
let p =
|
||||
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("js/main.ts");
|
||||
let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("js2/99_main.js");
|
||||
let specifier =
|
||||
ModuleSpecifier::resolve_url_or_path(p.to_str().unwrap()).unwrap();
|
||||
let r = fetcher
|
||||
|
@ -1484,8 +1484,8 @@ mod tests {
|
|||
.await;
|
||||
assert!(r.is_err());
|
||||
|
||||
let p =
|
||||
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("js/main.ts");
|
||||
let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("js2/99_main.js");
|
||||
let specifier =
|
||||
ModuleSpecifier::resolve_url_or_path(p.to_str().unwrap()).unwrap();
|
||||
let r = fetcher
|
||||
|
|
14
cli/js.rs
14
cli/js.rs
|
@ -2,19 +2,13 @@ pub const TS_VERSION: &str = env!("TS_VERSION");
|
|||
|
||||
pub static CLI_SNAPSHOT: &[u8] =
|
||||
include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.bin"));
|
||||
pub static CLI_SNAPSHOT_MAP: &[u8] =
|
||||
include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.js.map"));
|
||||
|
||||
pub static COMPILER_SNAPSHOT: &[u8] =
|
||||
include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.bin"));
|
||||
pub static COMPILER_SNAPSHOT_MAP: &[u8] =
|
||||
include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.js.map"));
|
||||
|
||||
pub static DENO_NS_LIB: &str = include_str!("js/lib.deno.ns.d.ts");
|
||||
pub static DENO_NS_LIB: &str = include_str!("js2/lib.deno.ns.d.ts");
|
||||
pub static SHARED_GLOBALS_LIB: &str =
|
||||
include_str!("js/lib.deno.shared_globals.d.ts");
|
||||
pub static WINDOW_LIB: &str = include_str!("js/lib.deno.window.d.ts");
|
||||
pub static UNSTABLE_NS_LIB: &str = include_str!("js/lib.deno.unstable.d.ts");
|
||||
include_str!("js2/lib.deno.shared_globals.d.ts");
|
||||
pub static WINDOW_LIB: &str = include_str!("js2/lib.deno.window.d.ts");
|
||||
pub static UNSTABLE_NS_LIB: &str = include_str!("js2/lib.deno.unstable.d.ts");
|
||||
|
||||
#[test]
|
||||
fn cli_snapshot() {
|
||||
|
|
232
cli/js/buffer.ts
232
cli/js/buffer.ts
|
@ -1,232 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// This code has been ported almost directly from Go's src/bytes/buffer.go
|
||||
// Copyright 2009 The Go Authors. All rights reserved. BSD license.
|
||||
// https://github.com/golang/go/blob/master/LICENSE
|
||||
|
||||
import type { Reader, Writer, ReaderSync, WriterSync } from "./io.ts";
|
||||
import { assert } from "./util.ts";
|
||||
|
||||
// MIN_READ is the minimum ArrayBuffer size passed to a read call by
|
||||
// buffer.ReadFrom. As long as the Buffer has at least MIN_READ bytes beyond
|
||||
// what is required to hold the contents of r, readFrom() will not grow the
|
||||
// underlying buffer.
|
||||
const MIN_READ = 32 * 1024;
|
||||
const MAX_SIZE = 2 ** 32 - 2;
|
||||
|
||||
// `off` is the offset into `dst` where it will at which to begin writing values
|
||||
// from `src`.
|
||||
// Returns the number of bytes copied.
|
||||
function copyBytes(src: Uint8Array, dst: Uint8Array, off = 0): number {
|
||||
const r = dst.byteLength - off;
|
||||
if (src.byteLength > r) {
|
||||
src = src.subarray(0, r);
|
||||
}
|
||||
dst.set(src, off);
|
||||
return src.byteLength;
|
||||
}
|
||||
|
||||
export class Buffer implements Reader, ReaderSync, Writer, WriterSync {
|
||||
#buf: Uint8Array; // contents are the bytes buf[off : len(buf)]
|
||||
#off = 0; // read at buf[off], write at buf[buf.byteLength]
|
||||
|
||||
constructor(ab?: ArrayBuffer) {
|
||||
if (ab == null) {
|
||||
this.#buf = new Uint8Array(0);
|
||||
return;
|
||||
}
|
||||
|
||||
this.#buf = new Uint8Array(ab);
|
||||
}
|
||||
|
||||
bytes(options: { copy?: boolean } = { copy: true }): Uint8Array {
|
||||
if (options.copy === false) return this.#buf.subarray(this.#off);
|
||||
return this.#buf.slice(this.#off);
|
||||
}
|
||||
|
||||
empty(): boolean {
|
||||
return this.#buf.byteLength <= this.#off;
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.#buf.byteLength - this.#off;
|
||||
}
|
||||
|
||||
get capacity(): number {
|
||||
return this.#buf.buffer.byteLength;
|
||||
}
|
||||
|
||||
truncate(n: number): void {
|
||||
if (n === 0) {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
if (n < 0 || n > this.length) {
|
||||
throw Error("bytes.Buffer: truncation out of range");
|
||||
}
|
||||
this.#reslice(this.#off + n);
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.#reslice(0);
|
||||
this.#off = 0;
|
||||
}
|
||||
|
||||
#tryGrowByReslice = (n: number): number => {
|
||||
const l = this.#buf.byteLength;
|
||||
if (n <= this.capacity - l) {
|
||||
this.#reslice(l + n);
|
||||
return l;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
#reslice = (len: number): void => {
|
||||
assert(len <= this.#buf.buffer.byteLength);
|
||||
this.#buf = new Uint8Array(this.#buf.buffer, 0, len);
|
||||
};
|
||||
|
||||
readSync(p: Uint8Array): number | null {
|
||||
if (this.empty()) {
|
||||
// Buffer is empty, reset to recover space.
|
||||
this.reset();
|
||||
if (p.byteLength === 0) {
|
||||
// this edge case is tested in 'bufferReadEmptyAtEOF' test
|
||||
return 0;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const nread = copyBytes(this.#buf.subarray(this.#off), p);
|
||||
this.#off += nread;
|
||||
return nread;
|
||||
}
|
||||
|
||||
read(p: Uint8Array): Promise<number | null> {
|
||||
const rr = this.readSync(p);
|
||||
return Promise.resolve(rr);
|
||||
}
|
||||
|
||||
writeSync(p: Uint8Array): number {
|
||||
const m = this.#grow(p.byteLength);
|
||||
return copyBytes(p, this.#buf, m);
|
||||
}
|
||||
|
||||
write(p: Uint8Array): Promise<number> {
|
||||
const n = this.writeSync(p);
|
||||
return Promise.resolve(n);
|
||||
}
|
||||
|
||||
#grow = (n: number): number => {
|
||||
const m = this.length;
|
||||
// If buffer is empty, reset to recover space.
|
||||
if (m === 0 && this.#off !== 0) {
|
||||
this.reset();
|
||||
}
|
||||
// Fast: Try to grow by means of a reslice.
|
||||
const i = this.#tryGrowByReslice(n);
|
||||
if (i >= 0) {
|
||||
return i;
|
||||
}
|
||||
const c = this.capacity;
|
||||
if (n <= Math.floor(c / 2) - m) {
|
||||
// We can slide things down instead of allocating a new
|
||||
// ArrayBuffer. We only need m+n <= c to slide, but
|
||||
// we instead let capacity get twice as large so we
|
||||
// don't spend all our time copying.
|
||||
copyBytes(this.#buf.subarray(this.#off), this.#buf);
|
||||
} else if (c + n > MAX_SIZE) {
|
||||
throw new Error("The buffer cannot be grown beyond the maximum size.");
|
||||
} else {
|
||||
// Not enough space anywhere, we need to allocate.
|
||||
const buf = new Uint8Array(Math.min(2 * c + n, MAX_SIZE));
|
||||
copyBytes(this.#buf.subarray(this.#off), buf);
|
||||
this.#buf = buf;
|
||||
}
|
||||
// Restore this.#off and len(this.#buf).
|
||||
this.#off = 0;
|
||||
this.#reslice(Math.min(m + n, MAX_SIZE));
|
||||
return m;
|
||||
};
|
||||
|
||||
grow(n: number): void {
|
||||
if (n < 0) {
|
||||
throw Error("Buffer.grow: negative count");
|
||||
}
|
||||
const m = this.#grow(n);
|
||||
this.#reslice(m);
|
||||
}
|
||||
|
||||
async readFrom(r: Reader): Promise<number> {
|
||||
let n = 0;
|
||||
const tmp = new Uint8Array(MIN_READ);
|
||||
while (true) {
|
||||
const shouldGrow = this.capacity - this.length < MIN_READ;
|
||||
// read into tmp buffer if there's not enough room
|
||||
// otherwise read directly into the internal buffer
|
||||
const buf = shouldGrow
|
||||
? tmp
|
||||
: new Uint8Array(this.#buf.buffer, this.length);
|
||||
|
||||
const nread = await r.read(buf);
|
||||
if (nread === null) {
|
||||
return n;
|
||||
}
|
||||
|
||||
// write will grow if needed
|
||||
if (shouldGrow) this.writeSync(buf.subarray(0, nread));
|
||||
else this.#reslice(this.length + nread);
|
||||
|
||||
n += nread;
|
||||
}
|
||||
}
|
||||
|
||||
readFromSync(r: ReaderSync): number {
|
||||
let n = 0;
|
||||
const tmp = new Uint8Array(MIN_READ);
|
||||
while (true) {
|
||||
const shouldGrow = this.capacity - this.length < MIN_READ;
|
||||
// read into tmp buffer if there's not enough room
|
||||
// otherwise read directly into the internal buffer
|
||||
const buf = shouldGrow
|
||||
? tmp
|
||||
: new Uint8Array(this.#buf.buffer, this.length);
|
||||
|
||||
const nread = r.readSync(buf);
|
||||
if (nread === null) {
|
||||
return n;
|
||||
}
|
||||
|
||||
// write will grow if needed
|
||||
if (shouldGrow) this.writeSync(buf.subarray(0, nread));
|
||||
else this.#reslice(this.length + nread);
|
||||
|
||||
n += nread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function readAll(r: Reader): Promise<Uint8Array> {
|
||||
const buf = new Buffer();
|
||||
await buf.readFrom(r);
|
||||
return buf.bytes();
|
||||
}
|
||||
|
||||
export function readAllSync(r: ReaderSync): Uint8Array {
|
||||
const buf = new Buffer();
|
||||
buf.readFromSync(r);
|
||||
return buf.bytes();
|
||||
}
|
||||
|
||||
export async function writeAll(w: Writer, arr: Uint8Array): Promise<void> {
|
||||
let nwritten = 0;
|
||||
while (nwritten < arr.length) {
|
||||
nwritten += await w.write(arr.subarray(nwritten));
|
||||
}
|
||||
}
|
||||
|
||||
export function writeAllSync(w: WriterSync, arr: Uint8Array): void {
|
||||
let nwritten = 0;
|
||||
while (nwritten < arr.length) {
|
||||
nwritten += w.writeSync(arr.subarray(nwritten));
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
export const build = {
|
||||
target: "unknown",
|
||||
arch: "unknown",
|
||||
os: "unknown",
|
||||
vendor: "unknown",
|
||||
env: undefined as string | undefined,
|
||||
};
|
||||
|
||||
export function setBuildInfo(target: string): void {
|
||||
const [arch, vendor, os, env] = target.split("-", 4);
|
||||
build.target = target;
|
||||
build.arch = arch;
|
||||
build.vendor = vendor;
|
||||
build.os = os;
|
||||
build.env = env;
|
||||
Object.freeze(build);
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
interface Code {
|
||||
open: string;
|
||||
close: string;
|
||||
regexp: RegExp;
|
||||
}
|
||||
|
||||
function code(open: number, close: number): Code {
|
||||
return {
|
||||
open: `\x1b[${open}m`,
|
||||
close: `\x1b[${close}m`,
|
||||
regexp: new RegExp(`\\x1b\\[${close}m`, "g"),
|
||||
};
|
||||
}
|
||||
|
||||
function run(str: string, code: Code): string {
|
||||
return !globalThis || !globalThis.Deno || globalThis.Deno.noColor
|
||||
? str
|
||||
: `${code.open}${str.replace(code.regexp, code.open)}${code.close}`;
|
||||
}
|
||||
|
||||
export function bold(str: string): string {
|
||||
return run(str, code(1, 22));
|
||||
}
|
||||
|
||||
export function italic(str: string): string {
|
||||
return run(str, code(3, 23));
|
||||
}
|
||||
|
||||
export function yellow(str: string): string {
|
||||
return run(str, code(33, 39));
|
||||
}
|
||||
|
||||
export function cyan(str: string): string {
|
||||
return run(str, code(36, 39));
|
||||
}
|
||||
|
||||
export function red(str: string): string {
|
||||
return run(str, code(31, 39));
|
||||
}
|
||||
|
||||
export function green(str: string): string {
|
||||
return run(str, code(32, 39));
|
||||
}
|
||||
|
||||
export function bgRed(str: string): string {
|
||||
return run(str, code(41, 49));
|
||||
}
|
||||
|
||||
export function white(str: string): string {
|
||||
return run(str, code(37, 39));
|
||||
}
|
||||
|
||||
export function gray(str: string): string {
|
||||
return run(str, code(90, 39));
|
||||
}
|
||||
|
||||
export function magenta(str: string): string {
|
||||
return run(str, code(35, 39));
|
||||
}
|
||||
|
||||
export function dim(str: string): string {
|
||||
return run(str, code(2, 22));
|
||||
}
|
||||
|
||||
// https://github.com/chalk/ansi-regex/blob/2b56fb0c7a07108e5b54241e8faec160d393aedb/index.js
|
||||
const ANSI_PATTERN = new RegExp(
|
||||
[
|
||||
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
|
||||
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))",
|
||||
].join("|"),
|
||||
"g",
|
||||
);
|
||||
|
||||
export function stripColor(string: string): string {
|
||||
return string.replace(ANSI_PATTERN, "");
|
||||
}
|
1838
cli/js/compiler.ts
1838
cli/js/compiler.ts
File diff suppressed because it is too large
Load diff
|
@ -1,86 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// This file contains the runtime APIs which will dispatch work to the internal
|
||||
// compiler within Deno.
|
||||
|
||||
import type { DiagnosticItem } from "./diagnostics.ts";
|
||||
import * as util from "./util.ts";
|
||||
import * as runtimeCompilerOps from "./ops/runtime_compiler.ts";
|
||||
import type { TranspileOnlyResult } from "./ops/runtime_compiler.ts";
|
||||
import type { CompilerOptions } from "./compiler_options.ts";
|
||||
|
||||
function checkRelative(specifier: string): string {
|
||||
return specifier.match(/^([\.\/\\]|https?:\/{2}|file:\/{2})/)
|
||||
? specifier
|
||||
: `./${specifier}`;
|
||||
}
|
||||
|
||||
// TODO(bartlomieju): change return type to interface?
|
||||
export function transpileOnly(
|
||||
sources: Record<string, string>,
|
||||
options: CompilerOptions = {},
|
||||
): Promise<Record<string, TranspileOnlyResult>> {
|
||||
util.log("Deno.transpileOnly", { sources: Object.keys(sources), options });
|
||||
const payload = {
|
||||
sources,
|
||||
options: JSON.stringify(options),
|
||||
};
|
||||
return runtimeCompilerOps.transpile(payload);
|
||||
}
|
||||
|
||||
// TODO(bartlomieju): change return type to interface?
|
||||
export async function compile(
|
||||
rootName: string,
|
||||
sources?: Record<string, string>,
|
||||
options: CompilerOptions = {},
|
||||
): Promise<[DiagnosticItem[] | undefined, Record<string, string>]> {
|
||||
const payload = {
|
||||
rootName: sources ? rootName : checkRelative(rootName),
|
||||
sources,
|
||||
options: JSON.stringify(options),
|
||||
bundle: false,
|
||||
};
|
||||
util.log("Deno.compile", {
|
||||
rootName: payload.rootName,
|
||||
sources: !!sources,
|
||||
options,
|
||||
});
|
||||
const result = await runtimeCompilerOps.compile(payload);
|
||||
util.assert(result.emitMap);
|
||||
const maybeDiagnostics = result.diagnostics.length === 0
|
||||
? undefined
|
||||
: result.diagnostics;
|
||||
|
||||
const emitMap: Record<string, string> = {};
|
||||
|
||||
for (const [key, emittedSource] of Object.entries(result.emitMap)) {
|
||||
emitMap[key] = emittedSource.contents;
|
||||
}
|
||||
|
||||
return [maybeDiagnostics, emitMap];
|
||||
}
|
||||
|
||||
// TODO(bartlomieju): change return type to interface?
|
||||
export async function bundle(
|
||||
rootName: string,
|
||||
sources?: Record<string, string>,
|
||||
options: CompilerOptions = {},
|
||||
): Promise<[DiagnosticItem[] | undefined, string]> {
|
||||
const payload = {
|
||||
rootName: sources ? rootName : checkRelative(rootName),
|
||||
sources,
|
||||
options: JSON.stringify(options),
|
||||
bundle: true,
|
||||
};
|
||||
util.log("Deno.bundle", {
|
||||
rootName: payload.rootName,
|
||||
sources: !!sources,
|
||||
options,
|
||||
});
|
||||
const result = await runtimeCompilerOps.compile(payload);
|
||||
util.assert(result.output);
|
||||
const maybeDiagnostics = result.diagnostics.length === 0
|
||||
? undefined
|
||||
: result.diagnostics;
|
||||
return [maybeDiagnostics, result.output];
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
export interface CompilerOptions {
|
||||
allowJs?: boolean;
|
||||
|
||||
allowSyntheticDefaultImports?: boolean;
|
||||
|
||||
allowUmdGlobalAccess?: boolean;
|
||||
|
||||
allowUnreachableCode?: boolean;
|
||||
|
||||
allowUnusedLabels?: boolean;
|
||||
|
||||
alwaysStrict?: boolean;
|
||||
|
||||
baseUrl?: string;
|
||||
|
||||
checkJs?: boolean;
|
||||
|
||||
declaration?: boolean;
|
||||
|
||||
declarationDir?: string;
|
||||
|
||||
declarationMap?: boolean;
|
||||
|
||||
downlevelIteration?: boolean;
|
||||
|
||||
emitBOM?: boolean;
|
||||
|
||||
emitDeclarationOnly?: boolean;
|
||||
|
||||
emitDecoratorMetadata?: boolean;
|
||||
|
||||
esModuleInterop?: boolean;
|
||||
|
||||
experimentalDecorators?: boolean;
|
||||
|
||||
inlineSourceMap?: boolean;
|
||||
|
||||
inlineSources?: boolean;
|
||||
|
||||
isolatedModules?: boolean;
|
||||
|
||||
jsx?: "react" | "preserve" | "react-native";
|
||||
|
||||
jsxFactory?: string;
|
||||
|
||||
keyofStringsOnly?: string;
|
||||
|
||||
useDefineForClassFields?: boolean;
|
||||
|
||||
lib?: string[];
|
||||
|
||||
locale?: string;
|
||||
|
||||
mapRoot?: string;
|
||||
|
||||
module?:
|
||||
| "none"
|
||||
| "commonjs"
|
||||
| "amd"
|
||||
| "system"
|
||||
| "umd"
|
||||
| "es6"
|
||||
| "es2015"
|
||||
| "esnext";
|
||||
|
||||
noEmitHelpers?: boolean;
|
||||
|
||||
noFallthroughCasesInSwitch?: boolean;
|
||||
|
||||
noImplicitAny?: boolean;
|
||||
|
||||
noImplicitReturns?: boolean;
|
||||
|
||||
noImplicitThis?: boolean;
|
||||
|
||||
noImplicitUseStrict?: boolean;
|
||||
|
||||
noResolve?: boolean;
|
||||
|
||||
noStrictGenericChecks?: boolean;
|
||||
|
||||
noUnusedLocals?: boolean;
|
||||
|
||||
noUnusedParameters?: boolean;
|
||||
|
||||
outDir?: string;
|
||||
|
||||
paths?: Record<string, string[]>;
|
||||
|
||||
preserveConstEnums?: boolean;
|
||||
|
||||
removeComments?: boolean;
|
||||
|
||||
resolveJsonModule?: boolean;
|
||||
|
||||
rootDir?: string;
|
||||
|
||||
rootDirs?: string[];
|
||||
|
||||
sourceMap?: boolean;
|
||||
|
||||
sourceRoot?: string;
|
||||
|
||||
strict?: boolean;
|
||||
|
||||
strictBindCallApply?: boolean;
|
||||
|
||||
strictFunctionTypes?: boolean;
|
||||
|
||||
strictPropertyInitialization?: boolean;
|
||||
|
||||
strictNullChecks?: boolean;
|
||||
|
||||
suppressExcessPropertyErrors?: boolean;
|
||||
|
||||
suppressImplicitAnyIndexErrors?: boolean;
|
||||
|
||||
target?:
|
||||
| "es3"
|
||||
| "es5"
|
||||
| "es6"
|
||||
| "es2015"
|
||||
| "es2016"
|
||||
| "es2017"
|
||||
| "es2018"
|
||||
| "es2019"
|
||||
| "es2020"
|
||||
| "esnext";
|
||||
|
||||
types?: string[];
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// This allows us to access core in API even if we
|
||||
// dispose window.Deno
|
||||
export const core = globalThis.Deno.core as DenoCore;
|
|
@ -1,91 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// This module exports stable Deno APIs.
|
||||
|
||||
export {
|
||||
Buffer,
|
||||
readAll,
|
||||
readAllSync,
|
||||
writeAll,
|
||||
writeAllSync,
|
||||
} from "./buffer.ts";
|
||||
export { build } from "./build.ts";
|
||||
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 { chdir, cwd } from "./ops/fs/dir.ts";
|
||||
export { errors } from "./errors.ts";
|
||||
export {
|
||||
File,
|
||||
open,
|
||||
openSync,
|
||||
create,
|
||||
createSync,
|
||||
stdin,
|
||||
stdout,
|
||||
stderr,
|
||||
seek,
|
||||
seekSync,
|
||||
} from "./files.ts";
|
||||
export type { OpenOptions } from "./files.ts";
|
||||
export { read, readSync, write, writeSync } from "./ops/io.ts";
|
||||
export { watchFs } from "./ops/fs_events.ts";
|
||||
export type { FsEvent } from "./ops/fs_events.ts";
|
||||
export { internalSymbol as internal } from "./internals.ts";
|
||||
export { copy, iter, iterSync } from "./io.ts";
|
||||
export { SeekMode } from "./io.ts";
|
||||
export type {
|
||||
Reader,
|
||||
ReaderSync,
|
||||
Writer,
|
||||
WriterSync,
|
||||
Closer,
|
||||
Seeker,
|
||||
} from "./io.ts";
|
||||
export {
|
||||
makeTempDirSync,
|
||||
makeTempDir,
|
||||
makeTempFileSync,
|
||||
makeTempFile,
|
||||
} from "./ops/fs/make_temp.ts";
|
||||
export type { MakeTempOptions } from "./ops/fs/make_temp.ts";
|
||||
export { metrics } from "./ops/runtime.ts";
|
||||
export type { Metrics } from "./ops/runtime.ts";
|
||||
export { mkdirSync, mkdir } from "./ops/fs/mkdir.ts";
|
||||
export type { MkdirOptions } from "./ops/fs/mkdir.ts";
|
||||
export { connect, listen } from "./net.ts";
|
||||
export type { Listener, Conn } from "./net.ts";
|
||||
export { env, exit, execPath } from "./ops/os.ts";
|
||||
export { Process, run } from "./process.ts";
|
||||
export type { RunOptions, ProcessStatus } from "./process.ts";
|
||||
export { readDirSync, readDir } from "./ops/fs/read_dir.ts";
|
||||
export type { DirEntry } from "./ops/fs/read_dir.ts";
|
||||
export { readFileSync, readFile } from "./read_file.ts";
|
||||
export { readTextFileSync, readTextFile } from "./read_text_file.ts";
|
||||
export { readLinkSync, readLink } from "./ops/fs/read_link.ts";
|
||||
export { realPathSync, realPath } from "./ops/fs/real_path.ts";
|
||||
export { removeSync, remove } from "./ops/fs/remove.ts";
|
||||
export type { RemoveOptions } from "./ops/fs/remove.ts";
|
||||
export { renameSync, rename } from "./ops/fs/rename.ts";
|
||||
export { resources, close } from "./ops/resources.ts";
|
||||
export { statSync, lstatSync, stat, lstat } from "./ops/fs/stat.ts";
|
||||
export type { FileInfo } from "./ops/fs/stat.ts";
|
||||
export { connectTls, listenTls } from "./tls.ts";
|
||||
export { truncateSync, truncate } from "./ops/fs/truncate.ts";
|
||||
export { isatty } from "./ops/tty.ts";
|
||||
export { version } from "./version.ts";
|
||||
export { writeFileSync, writeFile } from "./write_file.ts";
|
||||
export type { WriteFileOptions } from "./write_file.ts";
|
||||
export { writeTextFileSync, writeTextFile } from "./write_text_file.ts";
|
||||
export const args: string[] = [];
|
||||
export { test } from "./testing.ts";
|
||||
export type { TestDefinition } from "./testing.ts";
|
||||
|
||||
// These are internal Deno APIs. We are marking them as internal so they do not
|
||||
// appear in the runtime type library.
|
||||
export { core } from "./core.ts";
|
||||
|
||||
export let pid: number;
|
||||
|
||||
export let noColor: boolean;
|
|
@ -1,30 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// This module exports unstable Deno APIs.
|
||||
|
||||
export { umask } from "./ops/fs/umask.ts";
|
||||
export { linkSync, link } from "./ops/fs/link.ts";
|
||||
export { fstatSync, fstat } from "./ops/fs/stat.ts";
|
||||
export { fdatasyncSync, fdatasync, fsyncSync, fsync } from "./ops/fs/sync.ts";
|
||||
export { symlinkSync, symlink } from "./ops/fs/symlink.ts";
|
||||
export { loadavg, osRelease, hostname } from "./ops/os.ts";
|
||||
export { openPlugin } from "./ops/plugins.ts";
|
||||
export { transpileOnly, compile, bundle } from "./compiler_api.ts";
|
||||
export { applySourceMap, formatDiagnostics } from "./ops/errors.ts";
|
||||
export { signal, signals, Signal, SignalStream } from "./signals.ts";
|
||||
export { setRaw, consoleSize } from "./ops/tty.ts";
|
||||
export { utimeSync, utime } from "./ops/fs/utime.ts";
|
||||
export { ftruncateSync, ftruncate } from "./ops/fs/truncate.ts";
|
||||
export { shutdown, ShutdownMode } from "./net.ts";
|
||||
export { listen, listenDatagram, connect } from "./net_unstable.ts";
|
||||
export { startTls } from "./tls.ts";
|
||||
export { kill } from "./ops/process.ts";
|
||||
export { permissions, Permissions } from "./permissions.ts";
|
||||
export { PermissionStatus } from "./permissions.ts";
|
||||
export type { PermissionName, PermissionState } from "./permissions.ts";
|
||||
export { DiagnosticCategory } from "./diagnostics.ts";
|
||||
export type {
|
||||
Diagnostic,
|
||||
DiagnosticItem,
|
||||
DiagnosticMessageChain,
|
||||
} from "./diagnostics.ts";
|
|
@ -1,51 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// Diagnostic provides an abstraction for advice/errors received from a
|
||||
// compiler, which is strongly influenced by the format of TypeScript
|
||||
// diagnostics.
|
||||
|
||||
export enum DiagnosticCategory {
|
||||
Log = 0,
|
||||
Debug = 1,
|
||||
Info = 2,
|
||||
Error = 3,
|
||||
Warning = 4,
|
||||
Suggestion = 5,
|
||||
}
|
||||
|
||||
export interface DiagnosticMessageChain {
|
||||
message: string;
|
||||
category: DiagnosticCategory;
|
||||
code: number;
|
||||
next?: DiagnosticMessageChain[];
|
||||
}
|
||||
|
||||
export interface DiagnosticItem {
|
||||
message: string;
|
||||
|
||||
messageChain?: DiagnosticMessageChain;
|
||||
|
||||
relatedInformation?: DiagnosticItem[];
|
||||
|
||||
sourceLine?: string;
|
||||
|
||||
lineNumber?: number;
|
||||
|
||||
scriptResourceName?: string;
|
||||
|
||||
startPosition?: number;
|
||||
|
||||
endPosition?: number;
|
||||
|
||||
category: DiagnosticCategory;
|
||||
|
||||
code: number;
|
||||
|
||||
startColumn?: number;
|
||||
|
||||
endColumn?: number;
|
||||
}
|
||||
|
||||
export interface Diagnostic {
|
||||
items: DiagnosticItem[];
|
||||
}
|
|
@ -1,252 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// These utilities are used by compiler.ts to format TypeScript diagnostics
|
||||
// into Deno Diagnostics.
|
||||
|
||||
import {
|
||||
Diagnostic,
|
||||
DiagnosticCategory,
|
||||
DiagnosticMessageChain,
|
||||
DiagnosticItem,
|
||||
} from "./diagnostics.ts";
|
||||
|
||||
const unstableDenoGlobalProperties = [
|
||||
"umask",
|
||||
"linkSync",
|
||||
"link",
|
||||
"symlinkSync",
|
||||
"symlink",
|
||||
"loadavg",
|
||||
"osRelease",
|
||||
"openPlugin",
|
||||
"DiagnosticCategory",
|
||||
"DiagnosticMessageChain",
|
||||
"DiagnosticItem",
|
||||
"Diagnostic",
|
||||
"formatDiagnostics",
|
||||
"CompilerOptions",
|
||||
"TranspileOnlyResult",
|
||||
"transpileOnly",
|
||||
"compile",
|
||||
"bundle",
|
||||
"Location",
|
||||
"applySourceMap",
|
||||
"LinuxSignal",
|
||||
"MacOSSignal",
|
||||
"Signal",
|
||||
"SignalStream",
|
||||
"signal",
|
||||
"signals",
|
||||
"setRaw",
|
||||
"utimeSync",
|
||||
"utime",
|
||||
"ShutdownMode",
|
||||
"shutdown",
|
||||
"DatagramConn",
|
||||
"UnixListenOptions",
|
||||
"listen",
|
||||
"listenDatagram",
|
||||
"UnixConnectOptions",
|
||||
"connect",
|
||||
"StartTlsOptions",
|
||||
"startTls",
|
||||
"kill",
|
||||
"PermissionName",
|
||||
"PermissionState",
|
||||
"RunPermissionDescriptor",
|
||||
"ReadPermissionDescriptor",
|
||||
"WritePermissionDescriptor",
|
||||
"NetPermissionDescriptor",
|
||||
"EnvPermissionDescriptor",
|
||||
"PluginPermissionDescriptor",
|
||||
"HrtimePermissionDescriptor",
|
||||
"PermissionDescriptor",
|
||||
"Permissions",
|
||||
"PermissionStatus",
|
||||
"hostname",
|
||||
"ppid",
|
||||
];
|
||||
|
||||
function transformMessageText(messageText: string, code: number): string {
|
||||
switch (code) {
|
||||
case 2339: {
|
||||
const property = messageText
|
||||
.replace(/^Property '/, "")
|
||||
.replace(/' does not exist on type 'typeof Deno'\./, "");
|
||||
|
||||
if (
|
||||
messageText.endsWith("on type 'typeof Deno'.") &&
|
||||
unstableDenoGlobalProperties.includes(property)
|
||||
) {
|
||||
return `${messageText} 'Deno.${property}' is an unstable API. Did you forget to run with the '--unstable' flag?`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2551: {
|
||||
const suggestionMessagePattern = / Did you mean '(.+)'\?$/;
|
||||
const property = messageText
|
||||
.replace(/^Property '/, "")
|
||||
.replace(/' does not exist on type 'typeof Deno'\./, "")
|
||||
.replace(suggestionMessagePattern, "");
|
||||
const suggestion = messageText.match(suggestionMessagePattern);
|
||||
const replacedMessageText = messageText.replace(
|
||||
suggestionMessagePattern,
|
||||
"",
|
||||
);
|
||||
if (suggestion && unstableDenoGlobalProperties.includes(property)) {
|
||||
const suggestedProperty = suggestion[1];
|
||||
return `${replacedMessageText} 'Deno.${property}' is an unstable API. Did you forget to run with the '--unstable' flag, or did you mean '${suggestedProperty}'?`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return messageText;
|
||||
}
|
||||
|
||||
interface SourceInformation {
|
||||
sourceLine: string;
|
||||
lineNumber: number;
|
||||
scriptResourceName: string;
|
||||
startColumn: number;
|
||||
endColumn: number;
|
||||
}
|
||||
|
||||
function fromDiagnosticCategory(
|
||||
category: ts.DiagnosticCategory,
|
||||
): DiagnosticCategory {
|
||||
switch (category) {
|
||||
case ts.DiagnosticCategory.Error:
|
||||
return DiagnosticCategory.Error;
|
||||
case ts.DiagnosticCategory.Message:
|
||||
return DiagnosticCategory.Info;
|
||||
case ts.DiagnosticCategory.Suggestion:
|
||||
return DiagnosticCategory.Suggestion;
|
||||
case ts.DiagnosticCategory.Warning:
|
||||
return DiagnosticCategory.Warning;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unexpected DiagnosticCategory: "${category}"/"${
|
||||
ts.DiagnosticCategory[category]
|
||||
}"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getSourceInformation(
|
||||
sourceFile: ts.SourceFile,
|
||||
start: number,
|
||||
length: number,
|
||||
): SourceInformation {
|
||||
const scriptResourceName = sourceFile.fileName;
|
||||
const {
|
||||
line: lineNumber,
|
||||
character: startColumn,
|
||||
} = sourceFile.getLineAndCharacterOfPosition(start);
|
||||
const endPosition = sourceFile.getLineAndCharacterOfPosition(start + length);
|
||||
const endColumn = lineNumber === endPosition.line
|
||||
? endPosition.character
|
||||
: startColumn;
|
||||
const lastLineInFile = sourceFile.getLineAndCharacterOfPosition(
|
||||
sourceFile.text.length,
|
||||
).line;
|
||||
const lineStart = sourceFile.getPositionOfLineAndCharacter(lineNumber, 0);
|
||||
const lineEnd = lineNumber < lastLineInFile
|
||||
? sourceFile.getPositionOfLineAndCharacter(lineNumber + 1, 0)
|
||||
: sourceFile.text.length;
|
||||
const sourceLine = sourceFile.text
|
||||
.slice(lineStart, lineEnd)
|
||||
.replace(/\s+$/g, "")
|
||||
.replace("\t", " ");
|
||||
return {
|
||||
sourceLine,
|
||||
lineNumber,
|
||||
scriptResourceName,
|
||||
startColumn,
|
||||
endColumn,
|
||||
};
|
||||
}
|
||||
|
||||
function fromDiagnosticMessageChain(
|
||||
messageChain: ts.DiagnosticMessageChain[] | undefined,
|
||||
): DiagnosticMessageChain[] | undefined {
|
||||
if (!messageChain) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return messageChain.map(({ messageText, code, category, next }) => {
|
||||
const message = transformMessageText(messageText, code);
|
||||
return {
|
||||
message,
|
||||
code,
|
||||
category: fromDiagnosticCategory(category),
|
||||
next: fromDiagnosticMessageChain(next),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function parseDiagnostic(
|
||||
item: ts.Diagnostic | ts.DiagnosticRelatedInformation,
|
||||
): DiagnosticItem {
|
||||
const {
|
||||
messageText,
|
||||
category: sourceCategory,
|
||||
code,
|
||||
file,
|
||||
start: startPosition,
|
||||
length,
|
||||
} = item;
|
||||
const sourceInfo = file && startPosition && length
|
||||
? getSourceInformation(file, startPosition, length)
|
||||
: undefined;
|
||||
const endPosition = startPosition && length
|
||||
? startPosition + length
|
||||
: undefined;
|
||||
const category = fromDiagnosticCategory(sourceCategory);
|
||||
|
||||
let message: string;
|
||||
let messageChain: DiagnosticMessageChain | undefined;
|
||||
if (typeof messageText === "string") {
|
||||
message = transformMessageText(messageText, code);
|
||||
} else {
|
||||
message = transformMessageText(messageText.messageText, messageText.code);
|
||||
messageChain = fromDiagnosticMessageChain([messageText])![0];
|
||||
}
|
||||
|
||||
const base = {
|
||||
message,
|
||||
messageChain,
|
||||
code,
|
||||
category,
|
||||
startPosition,
|
||||
endPosition,
|
||||
};
|
||||
|
||||
return sourceInfo ? { ...base, ...sourceInfo } : base;
|
||||
}
|
||||
|
||||
function parseRelatedInformation(
|
||||
relatedInformation: readonly ts.DiagnosticRelatedInformation[],
|
||||
): DiagnosticItem[] {
|
||||
const result: DiagnosticItem[] = [];
|
||||
for (const item of relatedInformation) {
|
||||
result.push(parseDiagnostic(item));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function fromTypeScriptDiagnostic(
|
||||
diagnostics: readonly ts.Diagnostic[],
|
||||
): Diagnostic {
|
||||
const items: DiagnosticItem[] = [];
|
||||
for (const sourceDiagnostic of diagnostics) {
|
||||
const item: DiagnosticItem = parseDiagnostic(sourceDiagnostic);
|
||||
if (sourceDiagnostic.relatedInformation) {
|
||||
item.relatedInformation = parseRelatedInformation(
|
||||
sourceDiagnostic.relatedInformation,
|
||||
);
|
||||
}
|
||||
items.push(item);
|
||||
}
|
||||
return { items };
|
||||
}
|
|
@ -1,265 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// Some of the code here is adapted directly from V8 and licensed under a BSD
|
||||
// style license available here: https://github.com/v8/v8/blob/24886f2d1c565287d33d71e4109a53bf0b54b75c/LICENSE.v8
|
||||
import * as colors from "./colors.ts";
|
||||
import { applySourceMap, Location } from "./ops/errors.ts";
|
||||
import { assert } from "./util.ts";
|
||||
import { exposeForTest } from "./internals.ts";
|
||||
|
||||
function patchCallSite(callSite: CallSite, location: Location): CallSite {
|
||||
return {
|
||||
getThis(): unknown {
|
||||
return callSite.getThis();
|
||||
},
|
||||
getTypeName(): string | null {
|
||||
return callSite.getTypeName();
|
||||
},
|
||||
getFunction(): Function | null {
|
||||
return callSite.getFunction();
|
||||
},
|
||||
getFunctionName(): string | null {
|
||||
return callSite.getFunctionName();
|
||||
},
|
||||
getMethodName(): string | null {
|
||||
return callSite.getMethodName();
|
||||
},
|
||||
getFileName(): string | null {
|
||||
return location.fileName;
|
||||
},
|
||||
getLineNumber(): number {
|
||||
return location.lineNumber;
|
||||
},
|
||||
getColumnNumber(): number {
|
||||
return location.columnNumber;
|
||||
},
|
||||
getEvalOrigin(): string | null {
|
||||
return callSite.getEvalOrigin();
|
||||
},
|
||||
isToplevel(): boolean | null {
|
||||
return callSite.isToplevel();
|
||||
},
|
||||
isEval(): boolean {
|
||||
return callSite.isEval();
|
||||
},
|
||||
isNative(): boolean {
|
||||
return callSite.isNative();
|
||||
},
|
||||
isConstructor(): boolean {
|
||||
return callSite.isConstructor();
|
||||
},
|
||||
isAsync(): boolean {
|
||||
return callSite.isAsync();
|
||||
},
|
||||
isPromiseAll(): boolean {
|
||||
return callSite.isPromiseAll();
|
||||
},
|
||||
getPromiseIndex(): number | null {
|
||||
return callSite.getPromiseIndex();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getMethodCall(callSite: CallSite): string {
|
||||
let result = "";
|
||||
|
||||
const typeName = callSite.getTypeName();
|
||||
const methodName = callSite.getMethodName();
|
||||
const functionName = callSite.getFunctionName();
|
||||
|
||||
if (functionName) {
|
||||
if (typeName) {
|
||||
const startsWithTypeName = functionName.startsWith(typeName);
|
||||
if (!startsWithTypeName) {
|
||||
result += `${typeName}.`;
|
||||
}
|
||||
}
|
||||
result += functionName;
|
||||
|
||||
if (methodName) {
|
||||
if (!functionName.endsWith(methodName)) {
|
||||
result += ` [as ${methodName}]`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (typeName) {
|
||||
result += `${typeName}.`;
|
||||
}
|
||||
if (methodName) {
|
||||
result += methodName;
|
||||
} else {
|
||||
result += "<anonymous>";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getFileLocation(callSite: CallSite, internal = false): string {
|
||||
const cyan = internal ? colors.gray : colors.cyan;
|
||||
const yellow = internal ? colors.gray : colors.yellow;
|
||||
const black = internal ? colors.gray : (s: string): string => s;
|
||||
if (callSite.isNative()) {
|
||||
return cyan("native");
|
||||
}
|
||||
|
||||
let result = "";
|
||||
|
||||
const fileName = callSite.getFileName();
|
||||
if (!fileName && callSite.isEval()) {
|
||||
const evalOrigin = callSite.getEvalOrigin();
|
||||
assert(evalOrigin != null);
|
||||
result += cyan(`${evalOrigin}, `);
|
||||
}
|
||||
|
||||
if (fileName) {
|
||||
result += cyan(fileName);
|
||||
} else {
|
||||
result += cyan("<anonymous>");
|
||||
}
|
||||
|
||||
const lineNumber = callSite.getLineNumber();
|
||||
if (lineNumber != null) {
|
||||
result += `${black(":")}${yellow(lineNumber.toString())}`;
|
||||
|
||||
const columnNumber = callSite.getColumnNumber();
|
||||
if (columnNumber != null) {
|
||||
result += `${black(":")}${yellow(columnNumber.toString())}`;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function callSiteToString(callSite: CallSite, internal = false): string {
|
||||
const cyan = internal ? colors.gray : colors.cyan;
|
||||
const black = internal ? colors.gray : (s: string): string => s;
|
||||
|
||||
let result = "";
|
||||
const functionName = callSite.getFunctionName();
|
||||
|
||||
const isTopLevel = callSite.isToplevel();
|
||||
const isAsync = callSite.isAsync();
|
||||
const isPromiseAll = callSite.isPromiseAll();
|
||||
const isConstructor = callSite.isConstructor();
|
||||
const isMethodCall = !(isTopLevel || isConstructor);
|
||||
|
||||
if (isAsync) {
|
||||
result += colors.gray("async ");
|
||||
}
|
||||
if (isPromiseAll) {
|
||||
result += colors.bold(
|
||||
colors.italic(black(`Promise.all (index ${callSite.getPromiseIndex()})`)),
|
||||
);
|
||||
return result;
|
||||
}
|
||||
if (isMethodCall) {
|
||||
result += colors.bold(colors.italic(black(getMethodCall(callSite))));
|
||||
} else if (isConstructor) {
|
||||
result += colors.gray("new ");
|
||||
if (functionName) {
|
||||
result += colors.bold(colors.italic(black(functionName)));
|
||||
} else {
|
||||
result += cyan("<anonymous>");
|
||||
}
|
||||
} else if (functionName) {
|
||||
result += colors.bold(colors.italic(black(functionName)));
|
||||
} else {
|
||||
result += getFileLocation(callSite, internal);
|
||||
return result;
|
||||
}
|
||||
|
||||
result += ` ${black("(")}${getFileLocation(callSite, internal)}${black(")")}`;
|
||||
return result;
|
||||
}
|
||||
|
||||
interface CallSiteEval {
|
||||
this: unknown;
|
||||
typeName: string | null;
|
||||
function: Function | null;
|
||||
functionName: string | null;
|
||||
methodName: string | null;
|
||||
fileName: string | null;
|
||||
lineNumber: number | null;
|
||||
columnNumber: number | null;
|
||||
evalOrigin: string | null;
|
||||
isToplevel: boolean | null;
|
||||
isEval: boolean;
|
||||
isNative: boolean;
|
||||
isConstructor: boolean;
|
||||
isAsync: boolean;
|
||||
isPromiseAll: boolean;
|
||||
promiseIndex: number | null;
|
||||
}
|
||||
|
||||
function evaluateCallSite(callSite: CallSite): CallSiteEval {
|
||||
return {
|
||||
this: callSite.getThis(),
|
||||
typeName: callSite.getTypeName(),
|
||||
function: callSite.getFunction(),
|
||||
functionName: callSite.getFunctionName(),
|
||||
methodName: callSite.getMethodName(),
|
||||
fileName: callSite.getFileName(),
|
||||
lineNumber: callSite.getLineNumber(),
|
||||
columnNumber: callSite.getColumnNumber(),
|
||||
evalOrigin: callSite.getEvalOrigin(),
|
||||
isToplevel: callSite.isToplevel(),
|
||||
isEval: callSite.isEval(),
|
||||
isNative: callSite.isNative(),
|
||||
isConstructor: callSite.isConstructor(),
|
||||
isAsync: callSite.isAsync(),
|
||||
isPromiseAll: callSite.isPromiseAll(),
|
||||
promiseIndex: callSite.getPromiseIndex(),
|
||||
};
|
||||
}
|
||||
|
||||
function prepareStackTrace(
|
||||
error: Error & {
|
||||
__callSiteEvals: CallSiteEval[];
|
||||
__formattedFrames: string[];
|
||||
},
|
||||
callSites: CallSite[],
|
||||
): string {
|
||||
const mappedCallSites = callSites.map(
|
||||
(callSite): CallSite => {
|
||||
const fileName = callSite.getFileName();
|
||||
const lineNumber = callSite.getLineNumber();
|
||||
const columnNumber = callSite.getColumnNumber();
|
||||
if (fileName && lineNumber != null && columnNumber != null) {
|
||||
return patchCallSite(
|
||||
callSite,
|
||||
applySourceMap({
|
||||
fileName,
|
||||
lineNumber,
|
||||
columnNumber,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return callSite;
|
||||
},
|
||||
);
|
||||
Object.defineProperties(error, {
|
||||
__callSiteEvals: { value: [], configurable: true },
|
||||
__formattedFrames: { value: [], configurable: true },
|
||||
});
|
||||
for (const callSite of mappedCallSites) {
|
||||
error.__callSiteEvals.push(Object.freeze(evaluateCallSite(callSite)));
|
||||
const isInternal = callSite.getFileName()?.startsWith("$deno$") ?? false;
|
||||
error.__formattedFrames.push(callSiteToString(callSite, isInternal));
|
||||
}
|
||||
Object.freeze(error.__callSiteEvals);
|
||||
Object.freeze(error.__formattedFrames);
|
||||
return (
|
||||
`${error.name}: ${error.message}\n` +
|
||||
error.__formattedFrames
|
||||
.map((s: string) => ` at ${colors.stripColor(s)}`)
|
||||
.join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
// @internal
|
||||
export function setPrepareStackTrace(ErrorConstructor: typeof Error): void {
|
||||
ErrorConstructor.prepareStackTrace = prepareStackTrace;
|
||||
}
|
||||
|
||||
exposeForTest("setPrepareStackTrace", setPrepareStackTrace);
|
225
cli/js/errors.ts
225
cli/js/errors.ts
|
@ -1,225 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// Warning! The values in this enum are duplicated in cli/op_error.rs
|
||||
// Update carefully!
|
||||
export enum ErrorKind {
|
||||
NotFound = 1,
|
||||
PermissionDenied = 2,
|
||||
ConnectionRefused = 3,
|
||||
ConnectionReset = 4,
|
||||
ConnectionAborted = 5,
|
||||
NotConnected = 6,
|
||||
AddrInUse = 7,
|
||||
AddrNotAvailable = 8,
|
||||
BrokenPipe = 9,
|
||||
AlreadyExists = 10,
|
||||
InvalidData = 13,
|
||||
TimedOut = 14,
|
||||
Interrupted = 15,
|
||||
WriteZero = 16,
|
||||
UnexpectedEof = 17,
|
||||
BadResource = 18,
|
||||
Http = 19,
|
||||
URIError = 20,
|
||||
TypeError = 21,
|
||||
Other = 22,
|
||||
Busy = 23,
|
||||
}
|
||||
|
||||
interface ErrorClass {
|
||||
new (msg: string): Error;
|
||||
}
|
||||
|
||||
export function getErrorClass(kind: ErrorKind): ErrorClass {
|
||||
switch (kind) {
|
||||
case ErrorKind.TypeError:
|
||||
return TypeError;
|
||||
case ErrorKind.Other:
|
||||
return Error;
|
||||
case ErrorKind.URIError:
|
||||
return URIError;
|
||||
case ErrorKind.NotFound:
|
||||
return NotFound;
|
||||
case ErrorKind.PermissionDenied:
|
||||
return PermissionDenied;
|
||||
case ErrorKind.ConnectionRefused:
|
||||
return ConnectionRefused;
|
||||
case ErrorKind.ConnectionReset:
|
||||
return ConnectionReset;
|
||||
case ErrorKind.ConnectionAborted:
|
||||
return ConnectionAborted;
|
||||
case ErrorKind.NotConnected:
|
||||
return NotConnected;
|
||||
case ErrorKind.AddrInUse:
|
||||
return AddrInUse;
|
||||
case ErrorKind.AddrNotAvailable:
|
||||
return AddrNotAvailable;
|
||||
case ErrorKind.BrokenPipe:
|
||||
return BrokenPipe;
|
||||
case ErrorKind.AlreadyExists:
|
||||
return AlreadyExists;
|
||||
case ErrorKind.InvalidData:
|
||||
return InvalidData;
|
||||
case ErrorKind.TimedOut:
|
||||
return TimedOut;
|
||||
case ErrorKind.Interrupted:
|
||||
return Interrupted;
|
||||
case ErrorKind.WriteZero:
|
||||
return WriteZero;
|
||||
case ErrorKind.UnexpectedEof:
|
||||
return UnexpectedEof;
|
||||
case ErrorKind.BadResource:
|
||||
return BadResource;
|
||||
case ErrorKind.Http:
|
||||
return Http;
|
||||
case ErrorKind.Busy:
|
||||
return Busy;
|
||||
}
|
||||
}
|
||||
|
||||
class NotFound extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "NotFound";
|
||||
}
|
||||
}
|
||||
|
||||
class PermissionDenied extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "PermissionDenied";
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionRefused extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "ConnectionRefused";
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionReset extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "ConnectionReset";
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionAborted extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "ConnectionAborted";
|
||||
}
|
||||
}
|
||||
|
||||
class NotConnected extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "NotConnected";
|
||||
}
|
||||
}
|
||||
|
||||
class AddrInUse extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "AddrInUse";
|
||||
}
|
||||
}
|
||||
|
||||
class AddrNotAvailable extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "AddrNotAvailable";
|
||||
}
|
||||
}
|
||||
|
||||
class BrokenPipe extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "BrokenPipe";
|
||||
}
|
||||
}
|
||||
|
||||
class AlreadyExists extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "AlreadyExists";
|
||||
}
|
||||
}
|
||||
|
||||
class InvalidData extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "InvalidData";
|
||||
}
|
||||
}
|
||||
|
||||
class TimedOut extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "TimedOut";
|
||||
}
|
||||
}
|
||||
|
||||
class Interrupted extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "Interrupted";
|
||||
}
|
||||
}
|
||||
|
||||
class WriteZero extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "WriteZero";
|
||||
}
|
||||
}
|
||||
|
||||
class UnexpectedEof extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "UnexpectedEof";
|
||||
}
|
||||
}
|
||||
|
||||
class BadResource extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "BadResource";
|
||||
}
|
||||
}
|
||||
|
||||
class Http extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "Http";
|
||||
}
|
||||
}
|
||||
|
||||
class Busy extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
this.name = "Busy";
|
||||
}
|
||||
}
|
||||
|
||||
export const errors = {
|
||||
NotFound,
|
||||
PermissionDenied,
|
||||
ConnectionRefused,
|
||||
ConnectionReset,
|
||||
ConnectionAborted,
|
||||
NotConnected,
|
||||
AddrInUse,
|
||||
AddrNotAvailable,
|
||||
BrokenPipe,
|
||||
AlreadyExists,
|
||||
InvalidData,
|
||||
TimedOut,
|
||||
Interrupted,
|
||||
WriteZero,
|
||||
UnexpectedEof,
|
||||
BadResource,
|
||||
Http,
|
||||
Busy,
|
||||
};
|
169
cli/js/files.ts
169
cli/js/files.ts
|
@ -1,169 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import type {
|
||||
Reader,
|
||||
Writer,
|
||||
Seeker,
|
||||
Closer,
|
||||
SeekMode,
|
||||
ReaderSync,
|
||||
WriterSync,
|
||||
SeekerSync,
|
||||
} from "./io.ts";
|
||||
import { close } from "./ops/resources.ts";
|
||||
import { read, readSync, write, writeSync } from "./ops/io.ts";
|
||||
import { seek, seekSync } from "./ops/fs/seek.ts";
|
||||
export { seek, seekSync } from "./ops/fs/seek.ts";
|
||||
import {
|
||||
open as opOpen,
|
||||
openSync as opOpenSync,
|
||||
OpenOptions,
|
||||
} from "./ops/fs/open.ts";
|
||||
export type { OpenOptions } from "./ops/fs/open.ts";
|
||||
|
||||
export function openSync(
|
||||
path: string | URL,
|
||||
options: OpenOptions = { read: true },
|
||||
): File {
|
||||
checkOpenOptions(options);
|
||||
const rid = opOpenSync(path, options);
|
||||
return new File(rid);
|
||||
}
|
||||
|
||||
export async function open(
|
||||
path: string | URL,
|
||||
options: OpenOptions = { read: true },
|
||||
): Promise<File> {
|
||||
checkOpenOptions(options);
|
||||
const rid = await opOpen(path, options);
|
||||
return new File(rid);
|
||||
}
|
||||
|
||||
export function createSync(path: string | URL): File {
|
||||
return openSync(path, {
|
||||
read: true,
|
||||
write: true,
|
||||
truncate: true,
|
||||
create: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function create(path: string | URL): Promise<File> {
|
||||
return open(path, {
|
||||
read: true,
|
||||
write: true,
|
||||
truncate: true,
|
||||
create: true,
|
||||
});
|
||||
}
|
||||
|
||||
export class File
|
||||
implements
|
||||
Reader,
|
||||
ReaderSync,
|
||||
Writer,
|
||||
WriterSync,
|
||||
Seeker,
|
||||
SeekerSync,
|
||||
Closer {
|
||||
constructor(readonly rid: number) {}
|
||||
|
||||
write(p: Uint8Array): Promise<number> {
|
||||
return write(this.rid, p);
|
||||
}
|
||||
|
||||
writeSync(p: Uint8Array): number {
|
||||
return writeSync(this.rid, p);
|
||||
}
|
||||
|
||||
read(p: Uint8Array): Promise<number | null> {
|
||||
return read(this.rid, p);
|
||||
}
|
||||
|
||||
readSync(p: Uint8Array): number | null {
|
||||
return readSync(this.rid, p);
|
||||
}
|
||||
|
||||
seek(offset: number, whence: SeekMode): Promise<number> {
|
||||
return seek(this.rid, offset, whence);
|
||||
}
|
||||
|
||||
seekSync(offset: number, whence: SeekMode): number {
|
||||
return seekSync(this.rid, offset, whence);
|
||||
}
|
||||
|
||||
close(): void {
|
||||
close(this.rid);
|
||||
}
|
||||
}
|
||||
|
||||
class Stdin implements Reader, ReaderSync, Closer {
|
||||
readonly rid = 0;
|
||||
|
||||
read(p: Uint8Array): Promise<number | null> {
|
||||
return read(this.rid, p);
|
||||
}
|
||||
|
||||
readSync(p: Uint8Array): number | null {
|
||||
return readSync(this.rid, p);
|
||||
}
|
||||
|
||||
close(): void {
|
||||
close(this.rid);
|
||||
}
|
||||
}
|
||||
|
||||
class Stdout implements Writer, WriterSync, Closer {
|
||||
readonly rid = 1;
|
||||
|
||||
write(p: Uint8Array): Promise<number> {
|
||||
return write(this.rid, p);
|
||||
}
|
||||
|
||||
writeSync(p: Uint8Array): number {
|
||||
return writeSync(this.rid, p);
|
||||
}
|
||||
|
||||
close(): void {
|
||||
close(this.rid);
|
||||
}
|
||||
}
|
||||
|
||||
export class Stderr implements Writer, WriterSync, Closer {
|
||||
readonly rid = 2;
|
||||
|
||||
write(p: Uint8Array): Promise<number> {
|
||||
return write(this.rid, p);
|
||||
}
|
||||
|
||||
writeSync(p: Uint8Array): number {
|
||||
return writeSync(this.rid, p);
|
||||
}
|
||||
|
||||
close(): void {
|
||||
close(this.rid);
|
||||
}
|
||||
}
|
||||
|
||||
export const stdin = new Stdin();
|
||||
export const stdout = new Stdout();
|
||||
export const stderr = new Stderr();
|
||||
|
||||
function checkOpenOptions(options: OpenOptions): void {
|
||||
if (Object.values(options).filter((val) => val === true).length === 0) {
|
||||
throw new Error("OpenOptions requires at least one option to be true");
|
||||
}
|
||||
|
||||
if (options.truncate && !options.write) {
|
||||
throw new Error("'truncate' option requires 'write' option");
|
||||
}
|
||||
|
||||
const createOrCreateNewWithoutWriteOrAppend =
|
||||
(options.create || options.createNew) && !(options.write || options.append);
|
||||
|
||||
if (createOrCreateNewWithoutWriteOrAppend) {
|
||||
throw new Error(
|
||||
"'create' or 'createNew' options require 'write' or 'append' option",
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,263 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import "./lib.deno.shared_globals.d.ts";
|
||||
|
||||
import * as abortController from "./web/abort_controller.ts";
|
||||
import * as abortSignal from "./web/abort_signal.ts";
|
||||
import * as blob from "./web/blob.ts";
|
||||
import * as consoleTypes from "./web/console.ts";
|
||||
import * as csprng from "./ops/get_random_values.ts";
|
||||
import type * as promiseTypes from "./web/promise.ts";
|
||||
import * as customEvent from "./web/custom_event.ts";
|
||||
import * as domException from "./web/dom_exception.ts";
|
||||
import * as domFile from "./web/dom_file.ts";
|
||||
import * as errorEvent from "./web/error_event.ts";
|
||||
import * as event from "./web/event.ts";
|
||||
import * as eventTarget from "./web/event_target.ts";
|
||||
import * as formData from "./web/form_data.ts";
|
||||
import * as fetchTypes from "./web/fetch.ts";
|
||||
import * as headers from "./web/headers.ts";
|
||||
import * as textEncoding from "./web/text_encoding.ts";
|
||||
import * as timers from "./web/timers.ts";
|
||||
import * as url from "./web/url.ts";
|
||||
import * as urlSearchParams from "./web/url_search_params.ts";
|
||||
import * as workers from "./web/workers.ts";
|
||||
import * as performance from "./web/performance.ts";
|
||||
import * as request from "./web/request.ts";
|
||||
import * as readableStream from "./web/streams/readable_stream.ts";
|
||||
import * as transformStream from "./web/streams/transform_stream.ts";
|
||||
import * as queuingStrategy from "./web/streams/queuing_strategy.ts";
|
||||
import * as writableStream from "./web/streams/writable_stream.ts";
|
||||
|
||||
// These imports are not exposed and therefore are fine to just import the
|
||||
// symbols required.
|
||||
import { core } from "./core.ts";
|
||||
|
||||
// This global augmentation is just enough types to be able to build Deno,
|
||||
// the runtime types are fully defined in `lib.deno.*.d.ts`.
|
||||
declare global {
|
||||
interface CallSite {
|
||||
getThis(): unknown;
|
||||
getTypeName(): string | null;
|
||||
getFunction(): Function | null;
|
||||
getFunctionName(): string | null;
|
||||
getMethodName(): string | null;
|
||||
getFileName(): string | null;
|
||||
getLineNumber(): number | null;
|
||||
getColumnNumber(): number | null;
|
||||
getEvalOrigin(): string | null;
|
||||
isToplevel(): boolean | null;
|
||||
isEval(): boolean;
|
||||
isNative(): boolean;
|
||||
isConstructor(): boolean;
|
||||
isAsync(): boolean;
|
||||
isPromiseAll(): boolean;
|
||||
getPromiseIndex(): number | null;
|
||||
}
|
||||
|
||||
interface ErrorConstructor {
|
||||
prepareStackTrace(error: Error, structuredStackTrace: CallSite[]): string;
|
||||
}
|
||||
|
||||
interface Object {
|
||||
[consoleTypes.customInspect]?(): string;
|
||||
}
|
||||
|
||||
interface EvalErrorInfo {
|
||||
isNativeError: boolean;
|
||||
isCompileError: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
thrown: any;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
url: string;
|
||||
main: boolean;
|
||||
}
|
||||
|
||||
interface DenoCore {
|
||||
print(s: string, isErr?: boolean): void;
|
||||
dispatch(opId: number, ...zeroCopy: ArrayBufferView[]): Uint8Array | null;
|
||||
dispatchByName(
|
||||
opName: string,
|
||||
...zeroCopy: ArrayBufferView[]
|
||||
): Uint8Array | null;
|
||||
setAsyncHandler(opId: number, cb: (msg: Uint8Array) => void): void;
|
||||
sharedQueue: {
|
||||
head(): number;
|
||||
numRecords(): number;
|
||||
size(): number;
|
||||
push(buf: Uint8Array): boolean;
|
||||
reset(): void;
|
||||
shift(): Uint8Array | null;
|
||||
};
|
||||
|
||||
ops(): Record<string, number>;
|
||||
|
||||
recv(cb: (opId: number, msg: Uint8Array) => void): void;
|
||||
|
||||
send(opId: number, ...data: ArrayBufferView[]): null | Uint8Array;
|
||||
|
||||
setMacrotaskCallback(cb: () => boolean): void;
|
||||
|
||||
shared: SharedArrayBuffer;
|
||||
|
||||
evalContext(
|
||||
code: string,
|
||||
scriptName?: string,
|
||||
): [unknown, EvalErrorInfo | null];
|
||||
|
||||
formatError: (e: Error) => string;
|
||||
|
||||
/**
|
||||
* Get promise details as two elements array.
|
||||
*
|
||||
* First element is the `PromiseState`.
|
||||
* If promise isn't pending, second element would be the result of the promise.
|
||||
* Otherwise, second element would be undefined.
|
||||
*
|
||||
* Throws `TypeError` if argument isn't a promise
|
||||
*
|
||||
*/
|
||||
getPromiseDetails<T>(promise: Promise<T>): promiseTypes.PromiseDetails<T>;
|
||||
|
||||
decode(bytes: Uint8Array): string;
|
||||
encode(text: string): Uint8Array;
|
||||
}
|
||||
|
||||
// Only `var` variables show up in the `globalThis` type when doing a global
|
||||
// scope augmentation.
|
||||
/* eslint-disable no-var */
|
||||
|
||||
// Assigned to `window` global - main runtime
|
||||
var Deno: {
|
||||
core: DenoCore;
|
||||
noColor: boolean;
|
||||
};
|
||||
var onload: ((e: Event) => void) | undefined;
|
||||
var onunload: ((e: Event) => void) | undefined;
|
||||
|
||||
// These methods are used to prepare different runtime
|
||||
// environments. After bootrapping, this namespace
|
||||
// should be removed from global scope.
|
||||
var bootstrap: {
|
||||
mainRuntime: (() => void) | undefined;
|
||||
// Assigned to `self` global - worker runtime and compiler
|
||||
workerRuntime: ((name: string) => Promise<void> | void) | undefined;
|
||||
// Assigned to `self` global - compiler
|
||||
tsCompilerRuntime: (() => void) | undefined;
|
||||
};
|
||||
|
||||
var onerror:
|
||||
| ((
|
||||
msg: string,
|
||||
source: string,
|
||||
lineno: number,
|
||||
colno: number,
|
||||
e: Event,
|
||||
) => boolean | void)
|
||||
| undefined;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
var onmessage: ((e: { data: any }) => Promise<void> | void) | undefined;
|
||||
// Called in compiler
|
||||
var close: () => void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
var postMessage: (msg: any) => void;
|
||||
/* eslint-enable */
|
||||
}
|
||||
|
||||
export function writable(value: unknown): PropertyDescriptor {
|
||||
return {
|
||||
value,
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function nonEnumerable(value: unknown): PropertyDescriptor {
|
||||
return {
|
||||
value,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function readOnly(value: unknown): PropertyDescriptor {
|
||||
return {
|
||||
value,
|
||||
enumerable: true,
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function getterOnly(getter: () => any): PropertyDescriptor {
|
||||
return {
|
||||
get: getter,
|
||||
enumerable: true,
|
||||
};
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
|
||||
export const windowOrWorkerGlobalScopeMethods = {
|
||||
atob: writable(textEncoding.atob),
|
||||
btoa: writable(textEncoding.btoa),
|
||||
clearInterval: writable(timers.clearInterval),
|
||||
clearTimeout: writable(timers.clearTimeout),
|
||||
fetch: writable(fetchTypes.fetch),
|
||||
// queueMicrotask is bound in Rust
|
||||
setInterval: writable(timers.setInterval),
|
||||
setTimeout: writable(timers.setTimeout),
|
||||
};
|
||||
|
||||
// Other properties shared between WindowScope and WorkerGlobalScope
|
||||
export const windowOrWorkerGlobalScopeProperties = {
|
||||
console: writable(new consoleTypes.Console(core.print)),
|
||||
AbortController: nonEnumerable(abortController.AbortControllerImpl),
|
||||
AbortSignal: nonEnumerable(abortSignal.AbortSignalImpl),
|
||||
Blob: nonEnumerable(blob.DenoBlob),
|
||||
ByteLengthQueuingStrategy: nonEnumerable(
|
||||
queuingStrategy.ByteLengthQueuingStrategyImpl,
|
||||
),
|
||||
CountQueuingStrategy: nonEnumerable(queuingStrategy.CountQueuingStrategyImpl),
|
||||
crypto: readOnly(csprng),
|
||||
File: nonEnumerable(domFile.DomFileImpl),
|
||||
CustomEvent: nonEnumerable(customEvent.CustomEventImpl),
|
||||
DOMException: nonEnumerable(domException.DOMExceptionImpl),
|
||||
ErrorEvent: nonEnumerable(errorEvent.ErrorEventImpl),
|
||||
Event: nonEnumerable(event.EventImpl),
|
||||
EventTarget: nonEnumerable(eventTarget.EventTargetImpl),
|
||||
Headers: nonEnumerable(headers.HeadersImpl),
|
||||
FormData: nonEnumerable(formData.FormDataImpl),
|
||||
ReadableStream: nonEnumerable(readableStream.ReadableStreamImpl),
|
||||
Request: nonEnumerable(request.Request),
|
||||
Response: nonEnumerable(fetchTypes.Response),
|
||||
performance: writable(new performance.PerformanceImpl()),
|
||||
Performance: nonEnumerable(performance.PerformanceImpl),
|
||||
PerformanceEntry: nonEnumerable(performance.PerformanceEntryImpl),
|
||||
PerformanceMark: nonEnumerable(performance.PerformanceMarkImpl),
|
||||
PerformanceMeasure: nonEnumerable(performance.PerformanceMeasureImpl),
|
||||
TextDecoder: nonEnumerable(textEncoding.TextDecoder),
|
||||
TextEncoder: nonEnumerable(textEncoding.TextEncoder),
|
||||
TransformStream: nonEnumerable(transformStream.TransformStreamImpl),
|
||||
URL: nonEnumerable(url.URLImpl),
|
||||
URLSearchParams: nonEnumerable(urlSearchParams.URLSearchParamsImpl),
|
||||
Worker: nonEnumerable(workers.WorkerImpl),
|
||||
WritableStream: nonEnumerable(writableStream.WritableStreamImpl),
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function setEventTargetData(value: any): void {
|
||||
eventTarget.eventTargetData.set(value, eventTarget.getDefaultTargetData());
|
||||
}
|
||||
|
||||
export const eventTargetProperties = {
|
||||
addEventListener: readOnly(
|
||||
eventTarget.EventTargetImpl.prototype.addEventListener,
|
||||
),
|
||||
dispatchEvent: readOnly(eventTarget.EventTargetImpl.prototype.dispatchEvent),
|
||||
removeEventListener: readOnly(
|
||||
eventTarget.EventTargetImpl.prototype.removeEventListener,
|
||||
),
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
export const unstableMethods = {};
|
||||
|
||||
export const unstableProperties = {};
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
export const internalSymbol = Symbol("Deno.internal");
|
||||
|
||||
// The object where all the internal fields for testing will be living.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const internalObject: Record<string, any> = {};
|
||||
|
||||
// Register a field to internalObject for test access,
|
||||
// through Deno[Deno.internal][name].
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function exposeForTest(name: string, value: any): void {
|
||||
Object.defineProperty(internalObject, name, {
|
||||
value,
|
||||
enumerable: false,
|
||||
});
|
||||
}
|
113
cli/js/io.ts
113
cli/js/io.ts
|
@ -1,113 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// Interfaces 100% copied from Go.
|
||||
// Documentation liberally lifted from them too.
|
||||
// Thank you! We love Go! <3
|
||||
|
||||
const DEFAULT_BUFFER_SIZE = 32 * 1024;
|
||||
|
||||
// Seek whence values.
|
||||
// https://golang.org/pkg/io/#pkg-constants
|
||||
export enum SeekMode {
|
||||
Start = 0,
|
||||
Current = 1,
|
||||
End = 2,
|
||||
}
|
||||
|
||||
// Reader is the interface that wraps the basic read() method.
|
||||
// https://golang.org/pkg/io/#Reader
|
||||
export interface Reader {
|
||||
read(p: Uint8Array): Promise<number | null>;
|
||||
}
|
||||
|
||||
export interface ReaderSync {
|
||||
readSync(p: Uint8Array): number | null;
|
||||
}
|
||||
|
||||
// Writer is the interface that wraps the basic write() method.
|
||||
// https://golang.org/pkg/io/#Writer
|
||||
export interface Writer {
|
||||
write(p: Uint8Array): Promise<number>;
|
||||
}
|
||||
|
||||
export interface WriterSync {
|
||||
writeSync(p: Uint8Array): number;
|
||||
}
|
||||
|
||||
// https://golang.org/pkg/io/#Closer
|
||||
export interface Closer {
|
||||
// The behavior of Close after the first call is undefined. Specific
|
||||
// implementations may document their own behavior.
|
||||
close(): void;
|
||||
}
|
||||
|
||||
// https://golang.org/pkg/io/#Seeker
|
||||
export interface Seeker {
|
||||
seek(offset: number, whence: SeekMode): Promise<number>;
|
||||
}
|
||||
|
||||
export interface SeekerSync {
|
||||
seekSync(offset: number, whence: SeekMode): number;
|
||||
}
|
||||
|
||||
export async function copy(
|
||||
src: Reader,
|
||||
dst: Writer,
|
||||
options?: {
|
||||
bufSize?: number;
|
||||
},
|
||||
): Promise<number> {
|
||||
let n = 0;
|
||||
const bufSize = options?.bufSize ?? DEFAULT_BUFFER_SIZE;
|
||||
const b = new Uint8Array(bufSize);
|
||||
let gotEOF = false;
|
||||
while (gotEOF === false) {
|
||||
const result = await src.read(b);
|
||||
if (result === null) {
|
||||
gotEOF = true;
|
||||
} else {
|
||||
let nwritten = 0;
|
||||
while (nwritten < result) {
|
||||
nwritten += await dst.write(b.subarray(nwritten, result));
|
||||
}
|
||||
n += nwritten;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
export async function* iter(
|
||||
r: Reader,
|
||||
options?: {
|
||||
bufSize?: number;
|
||||
},
|
||||
): AsyncIterableIterator<Uint8Array> {
|
||||
const bufSize = options?.bufSize ?? DEFAULT_BUFFER_SIZE;
|
||||
const b = new Uint8Array(bufSize);
|
||||
while (true) {
|
||||
const result = await r.read(b);
|
||||
if (result === null) {
|
||||
break;
|
||||
}
|
||||
|
||||
yield b.subarray(0, result);
|
||||
}
|
||||
}
|
||||
|
||||
export function* iterSync(
|
||||
r: ReaderSync,
|
||||
options?: {
|
||||
bufSize?: number;
|
||||
},
|
||||
): IterableIterator<Uint8Array> {
|
||||
const bufSize = options?.bufSize ?? DEFAULT_BUFFER_SIZE;
|
||||
const b = new Uint8Array(bufSize);
|
||||
while (true) {
|
||||
const result = r.readSync(b);
|
||||
if (result === null) {
|
||||
break;
|
||||
}
|
||||
|
||||
yield b.subarray(0, result);
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { bootstrapMainRuntime } from "./runtime_main.ts";
|
||||
import { bootstrapWorkerRuntime } from "./runtime_worker.ts";
|
||||
|
||||
// Removes the `__proto__` for security reasons. This intentionally makes
|
||||
// Deno non compliant with ECMA-262 Annex B.2.2.1
|
||||
//
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
delete (Object.prototype as any).__proto__;
|
||||
|
||||
Object.defineProperties(globalThis, {
|
||||
bootstrap: {
|
||||
value: {
|
||||
mainRuntime: bootstrapMainRuntime,
|
||||
workerRuntime: bootstrapWorkerRuntime,
|
||||
},
|
||||
configurable: true,
|
||||
writable: true,
|
||||
},
|
||||
});
|
192
cli/js/net.ts
192
cli/js/net.ts
|
@ -1,192 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { errors } from "./errors.ts";
|
||||
import type { Reader, Writer, Closer } from "./io.ts";
|
||||
import { read, write } from "./ops/io.ts";
|
||||
import { close } from "./ops/resources.ts";
|
||||
import * as netOps from "./ops/net.ts";
|
||||
import type { Addr } from "./ops/net.ts";
|
||||
export type { ShutdownMode, NetAddr, UnixAddr } from "./ops/net.ts";
|
||||
export { shutdown } from "./ops/net.ts";
|
||||
|
||||
export interface DatagramConn extends AsyncIterable<[Uint8Array, Addr]> {
|
||||
receive(p?: Uint8Array): Promise<[Uint8Array, Addr]>;
|
||||
|
||||
send(p: Uint8Array, addr: Addr): Promise<number>;
|
||||
|
||||
close(): void;
|
||||
|
||||
addr: Addr;
|
||||
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<[Uint8Array, Addr]>;
|
||||
}
|
||||
|
||||
export interface Listener extends AsyncIterable<Conn> {
|
||||
accept(): Promise<Conn>;
|
||||
|
||||
close(): void;
|
||||
|
||||
addr: Addr;
|
||||
|
||||
rid: number;
|
||||
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<Conn>;
|
||||
}
|
||||
|
||||
export class ConnImpl implements Conn {
|
||||
constructor(
|
||||
readonly rid: number,
|
||||
readonly remoteAddr: Addr,
|
||||
readonly localAddr: Addr,
|
||||
) {}
|
||||
|
||||
write(p: Uint8Array): Promise<number> {
|
||||
return write(this.rid, p);
|
||||
}
|
||||
|
||||
read(p: Uint8Array): Promise<number | null> {
|
||||
return read(this.rid, p);
|
||||
}
|
||||
|
||||
close(): void {
|
||||
close(this.rid);
|
||||
}
|
||||
|
||||
// TODO(lucacasonato): make this unavailable in stable
|
||||
closeWrite(): void {
|
||||
netOps.shutdown(this.rid, netOps.ShutdownMode.Write);
|
||||
}
|
||||
}
|
||||
|
||||
export class ListenerImpl implements Listener {
|
||||
constructor(readonly rid: number, readonly addr: Addr) {}
|
||||
|
||||
async accept(): Promise<Conn> {
|
||||
const res = await netOps.accept(this.rid, this.addr.transport);
|
||||
return new ConnImpl(res.rid, res.remoteAddr, res.localAddr);
|
||||
}
|
||||
|
||||
async next(): Promise<IteratorResult<Conn>> {
|
||||
let conn: Conn;
|
||||
try {
|
||||
conn = await this.accept();
|
||||
} catch (error) {
|
||||
if (error instanceof errors.BadResource) {
|
||||
return { value: undefined, done: true };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return { value: conn!, done: false };
|
||||
}
|
||||
|
||||
return(value?: Conn): Promise<IteratorResult<Conn>> {
|
||||
this.close();
|
||||
return Promise.resolve({ value, done: true });
|
||||
}
|
||||
|
||||
close(): void {
|
||||
close(this.rid);
|
||||
}
|
||||
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<Conn> {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class DatagramImpl implements DatagramConn {
|
||||
constructor(
|
||||
readonly rid: number,
|
||||
readonly addr: Addr,
|
||||
public bufSize: number = 1024,
|
||||
) {}
|
||||
|
||||
async receive(p?: Uint8Array): Promise<[Uint8Array, Addr]> {
|
||||
const buf = p || new Uint8Array(this.bufSize);
|
||||
const { size, remoteAddr } = await netOps.receive(
|
||||
this.rid,
|
||||
this.addr.transport,
|
||||
buf,
|
||||
);
|
||||
const sub = buf.subarray(0, size);
|
||||
return [sub, remoteAddr];
|
||||
}
|
||||
|
||||
send(p: Uint8Array, addr: Addr): Promise<number> {
|
||||
const remote = { hostname: "127.0.0.1", ...addr };
|
||||
|
||||
const args = { ...remote, rid: this.rid };
|
||||
return netOps.send(args as netOps.SendRequest, p);
|
||||
}
|
||||
|
||||
close(): void {
|
||||
close(this.rid);
|
||||
}
|
||||
|
||||
async *[Symbol.asyncIterator](): AsyncIterableIterator<[Uint8Array, Addr]> {
|
||||
while (true) {
|
||||
try {
|
||||
yield await this.receive();
|
||||
} catch (err) {
|
||||
if (err instanceof errors.BadResource) {
|
||||
break;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface Conn extends Reader, Writer, Closer {
|
||||
localAddr: Addr;
|
||||
remoteAddr: Addr;
|
||||
rid: number;
|
||||
closeWrite(): void;
|
||||
}
|
||||
|
||||
export interface ListenOptions {
|
||||
port: number;
|
||||
hostname?: string;
|
||||
transport?: "tcp";
|
||||
}
|
||||
|
||||
export function listen(
|
||||
options: ListenOptions & { transport?: "tcp" },
|
||||
): Listener;
|
||||
export function listen(options: ListenOptions): Listener {
|
||||
const res = netOps.listen({
|
||||
transport: "tcp",
|
||||
hostname: "0.0.0.0",
|
||||
...(options as ListenOptions),
|
||||
});
|
||||
|
||||
return new ListenerImpl(res.rid, res.localAddr);
|
||||
}
|
||||
|
||||
export interface ConnectOptions {
|
||||
port: number;
|
||||
hostname?: string;
|
||||
transport?: "tcp";
|
||||
}
|
||||
export interface UnixConnectOptions {
|
||||
transport: "unix";
|
||||
path: string;
|
||||
}
|
||||
export async function connect(options: UnixConnectOptions): Promise<Conn>;
|
||||
export async function connect(options: ConnectOptions): Promise<Conn>;
|
||||
export async function connect(
|
||||
options: ConnectOptions | UnixConnectOptions,
|
||||
): Promise<Conn> {
|
||||
let res;
|
||||
|
||||
if (options.transport === "unix") {
|
||||
res = await netOps.connect(options);
|
||||
} else {
|
||||
res = await netOps.connect({
|
||||
transport: "tcp",
|
||||
hostname: "127.0.0.1",
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!);
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import * as netOps from "./ops/net.ts";
|
||||
import {
|
||||
Listener,
|
||||
DatagramConn,
|
||||
ListenerImpl,
|
||||
DatagramImpl,
|
||||
ConnectOptions,
|
||||
Conn,
|
||||
ConnImpl,
|
||||
listen as stableListen,
|
||||
connect as stableConnect,
|
||||
} from "./net.ts";
|
||||
|
||||
export interface ListenOptions {
|
||||
port: number;
|
||||
hostname?: string;
|
||||
transport?: "tcp" | "udp";
|
||||
}
|
||||
|
||||
export interface UnixListenOptions {
|
||||
transport: "unix" | "unixpacket";
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface UnixConnectOptions {
|
||||
transport: "unix";
|
||||
path: string;
|
||||
}
|
||||
|
||||
export function listen(
|
||||
options: ListenOptions & { transport?: "tcp" },
|
||||
): Listener;
|
||||
export function listen(
|
||||
options: UnixListenOptions & { transport: "unix" },
|
||||
): Listener;
|
||||
export function listen(options: ListenOptions | UnixListenOptions): Listener {
|
||||
if (options.transport === "unix") {
|
||||
const res = netOps.listen(options);
|
||||
return new ListenerImpl(res.rid, res.localAddr);
|
||||
} else {
|
||||
return stableListen(options as ListenOptions & { transport?: "tcp" });
|
||||
}
|
||||
}
|
||||
|
||||
export function listenDatagram(
|
||||
options: ListenOptions & { transport: "udp" },
|
||||
): DatagramConn;
|
||||
export function listenDatagram(
|
||||
options: UnixListenOptions & { transport: "unixpacket" },
|
||||
): DatagramConn;
|
||||
export function listenDatagram(
|
||||
options: ListenOptions | UnixListenOptions,
|
||||
): DatagramConn {
|
||||
let res;
|
||||
if (options.transport === "unixpacket") {
|
||||
res = netOps.listen(options);
|
||||
} else {
|
||||
res = netOps.listen({
|
||||
transport: "udp",
|
||||
hostname: "127.0.0.1",
|
||||
...(options as ListenOptions),
|
||||
});
|
||||
}
|
||||
|
||||
return new DatagramImpl(res.rid, res.localAddr);
|
||||
}
|
||||
|
||||
export async function connect(
|
||||
options: ConnectOptions | UnixConnectOptions,
|
||||
): Promise<Conn> {
|
||||
if (options.transport === "unix") {
|
||||
const res = await netOps.connect(options);
|
||||
return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!);
|
||||
} else {
|
||||
return stableConnect(options as ConnectOptions);
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import * as util from "../util.ts";
|
||||
import { core } from "../core.ts";
|
||||
import { ErrorKind, getErrorClass } from "../errors.ts";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type Ok = any;
|
||||
|
||||
interface JsonError {
|
||||
kind: ErrorKind;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface JsonResponse {
|
||||
ok?: Ok;
|
||||
err?: JsonError;
|
||||
promiseId?: number; // Only present in async messages.
|
||||
}
|
||||
|
||||
// Using an object without a prototype because `Map` was causing GC problems.
|
||||
const promiseTable: Record<
|
||||
number,
|
||||
util.Resolvable<JsonResponse>
|
||||
> = Object.create(null);
|
||||
let _nextPromiseId = 1;
|
||||
|
||||
function nextPromiseId(): number {
|
||||
return _nextPromiseId++;
|
||||
}
|
||||
|
||||
function decode(ui8: Uint8Array): JsonResponse {
|
||||
return JSON.parse(core.decode(ui8));
|
||||
}
|
||||
|
||||
function encode(args: object): Uint8Array {
|
||||
return core.encode(JSON.stringify(args));
|
||||
}
|
||||
|
||||
function unwrapResponse(res: JsonResponse): Ok {
|
||||
if (res.err != null) {
|
||||
throw new (getErrorClass(res.err.kind))(res.err.message);
|
||||
}
|
||||
util.assert(res.ok != null);
|
||||
return res.ok;
|
||||
}
|
||||
|
||||
export function asyncMsgFromRust(resUi8: Uint8Array): void {
|
||||
const res = decode(resUi8);
|
||||
util.assert(res.promiseId != null);
|
||||
|
||||
const promise = promiseTable[res.promiseId!];
|
||||
util.assert(promise != null);
|
||||
delete promiseTable[res.promiseId!];
|
||||
promise.resolve(res);
|
||||
}
|
||||
|
||||
export function sendSync(
|
||||
opName: string,
|
||||
args: object = {},
|
||||
...zeroCopy: Uint8Array[]
|
||||
): Ok {
|
||||
util.log("sendSync", opName);
|
||||
const argsUi8 = encode(args);
|
||||
const resUi8 = core.dispatchByName(opName, argsUi8, ...zeroCopy);
|
||||
util.assert(resUi8 != null);
|
||||
const res = decode(resUi8);
|
||||
util.assert(res.promiseId == null);
|
||||
return unwrapResponse(res);
|
||||
}
|
||||
|
||||
export async function sendAsync(
|
||||
opName: string,
|
||||
args: object = {},
|
||||
...zeroCopy: Uint8Array[]
|
||||
): Promise<Ok> {
|
||||
util.log("sendAsync", opName);
|
||||
const promiseId = nextPromiseId();
|
||||
args = Object.assign(args, { promiseId });
|
||||
const promise = util.createResolvable<Ok>();
|
||||
const argsUi8 = encode(args);
|
||||
const buf = core.dispatchByName(opName, argsUi8, ...zeroCopy);
|
||||
if (buf != null) {
|
||||
// Sync result.
|
||||
const res = decode(buf);
|
||||
promise.resolve(res);
|
||||
} else {
|
||||
// Async result.
|
||||
promiseTable[promiseId] = promise;
|
||||
}
|
||||
|
||||
const res = await promise;
|
||||
return unwrapResponse(res);
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import * as util from "../util.ts";
|
||||
import { core } from "../core.ts";
|
||||
import { TextDecoder } from "../web/text_encoding.ts";
|
||||
import { ErrorKind, errors, getErrorClass } from "../errors.ts";
|
||||
|
||||
// Using an object without a prototype because `Map` was causing GC problems.
|
||||
const promiseTableMin: Record<
|
||||
number,
|
||||
util.Resolvable<RecordMinimal>
|
||||
> = Object.create(null);
|
||||
|
||||
// Note it's important that promiseId starts at 1 instead of 0, because sync
|
||||
// messages are indicated with promiseId 0. If we ever add wrap around logic for
|
||||
// overflows, this should be taken into account.
|
||||
let _nextPromiseId = 1;
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
function nextPromiseId(): number {
|
||||
return _nextPromiseId++;
|
||||
}
|
||||
|
||||
export interface RecordMinimal {
|
||||
promiseId: number;
|
||||
arg: number;
|
||||
result: number;
|
||||
err?: {
|
||||
kind: ErrorKind;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function recordFromBufMinimal(ui8: Uint8Array): RecordMinimal {
|
||||
const header = ui8.subarray(0, 12);
|
||||
const buf32 = new Int32Array(
|
||||
header.buffer,
|
||||
header.byteOffset,
|
||||
header.byteLength / 4,
|
||||
);
|
||||
const promiseId = buf32[0];
|
||||
const arg = buf32[1];
|
||||
const result = buf32[2];
|
||||
let err;
|
||||
|
||||
if (arg < 0) {
|
||||
const kind = result as ErrorKind;
|
||||
const message = decoder.decode(ui8.subarray(12));
|
||||
err = { kind, message };
|
||||
} else if (ui8.length != 12) {
|
||||
throw new errors.InvalidData("BadMessage");
|
||||
}
|
||||
|
||||
return {
|
||||
promiseId,
|
||||
arg,
|
||||
result,
|
||||
err,
|
||||
};
|
||||
}
|
||||
|
||||
function unwrapResponse(res: RecordMinimal): number {
|
||||
if (res.err != null) {
|
||||
throw new (getErrorClass(res.err.kind))(res.err.message);
|
||||
}
|
||||
return res.result;
|
||||
}
|
||||
|
||||
const scratch32 = new Int32Array(3);
|
||||
const scratchBytes = new Uint8Array(
|
||||
scratch32.buffer,
|
||||
scratch32.byteOffset,
|
||||
scratch32.byteLength,
|
||||
);
|
||||
util.assert(scratchBytes.byteLength === scratch32.length * 4);
|
||||
|
||||
export function asyncMsgFromRust(ui8: Uint8Array): void {
|
||||
const record = recordFromBufMinimal(ui8);
|
||||
const { promiseId } = record;
|
||||
const promise = promiseTableMin[promiseId];
|
||||
delete promiseTableMin[promiseId];
|
||||
util.assert(promise);
|
||||
promise.resolve(record);
|
||||
}
|
||||
|
||||
export async function sendAsyncMinimal(
|
||||
opName: string,
|
||||
arg: number,
|
||||
zeroCopy: Uint8Array,
|
||||
): Promise<number> {
|
||||
const promiseId = nextPromiseId(); // AKA cmdId
|
||||
scratch32[0] = promiseId;
|
||||
scratch32[1] = arg;
|
||||
scratch32[2] = 0; // result
|
||||
const promise = util.createResolvable<RecordMinimal>();
|
||||
const buf = core.dispatchByName(opName, scratchBytes, zeroCopy);
|
||||
if (buf != null) {
|
||||
const record = recordFromBufMinimal(buf);
|
||||
// Sync result.
|
||||
promise.resolve(record);
|
||||
} else {
|
||||
// Async result.
|
||||
promiseTableMin[promiseId] = promise;
|
||||
}
|
||||
|
||||
const res = await promise;
|
||||
return unwrapResponse(res);
|
||||
}
|
||||
|
||||
export function sendSyncMinimal(
|
||||
opName: string,
|
||||
arg: number,
|
||||
zeroCopy: Uint8Array,
|
||||
): number {
|
||||
scratch32[0] = 0; // promiseId 0 indicates sync
|
||||
scratch32[1] = arg;
|
||||
const res = core.dispatchByName(opName, scratchBytes, zeroCopy)!;
|
||||
const resRecord = recordFromBufMinimal(res);
|
||||
return unwrapResponse(resRecord);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import type { DiagnosticItem } from "../diagnostics.ts";
|
||||
import { sendSync } from "./dispatch_json.ts";
|
||||
|
||||
export function formatDiagnostics(items: DiagnosticItem[]): string {
|
||||
return sendSync("op_format_diagnostic", { items });
|
||||
}
|
||||
|
||||
export interface Location {
|
||||
fileName: string;
|
||||
lineNumber: number;
|
||||
columnNumber: number;
|
||||
}
|
||||
|
||||
export function applySourceMap(location: Location): Location {
|
||||
const res = sendSync("op_apply_source_map", location);
|
||||
return {
|
||||
fileName: res.fileName,
|
||||
lineNumber: res.lineNumber,
|
||||
columnNumber: res.columnNumber,
|
||||
};
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendAsync } from "./dispatch_json.ts";
|
||||
|
||||
interface FetchRequest {
|
||||
url: string;
|
||||
method: string | null;
|
||||
headers: Array<[string, string]>;
|
||||
}
|
||||
|
||||
export interface FetchResponse {
|
||||
bodyRid: number;
|
||||
status: number;
|
||||
statusText: string;
|
||||
headers: Array<[string, string]>;
|
||||
}
|
||||
|
||||
export function fetch(
|
||||
args: FetchRequest,
|
||||
body?: ArrayBufferView,
|
||||
): Promise<FetchResponse> {
|
||||
let zeroCopy;
|
||||
if (body != null) {
|
||||
zeroCopy = new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
|
||||
}
|
||||
|
||||
return sendAsync("op_fetch", args, ...(zeroCopy ? [zeroCopy] : []));
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
import { pathFromURL } from "../../util.ts";
|
||||
|
||||
export function chmodSync(path: string | URL, mode: number): void {
|
||||
sendSync("op_chmod", { path: pathFromURL(path), mode });
|
||||
}
|
||||
|
||||
export async function chmod(path: string | URL, mode: number): Promise<void> {
|
||||
await sendAsync("op_chmod", { path: pathFromURL(path), mode });
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
import { pathFromURL } from "../../util.ts";
|
||||
|
||||
export function chownSync(
|
||||
path: string | URL,
|
||||
uid: number | null,
|
||||
gid: number | null,
|
||||
): void {
|
||||
sendSync("op_chown", { path: pathFromURL(path), uid, gid });
|
||||
}
|
||||
|
||||
export async function chown(
|
||||
path: string | URL,
|
||||
uid: number | null,
|
||||
gid: number | null,
|
||||
): Promise<void> {
|
||||
await sendAsync("op_chown", { path: pathFromURL(path), uid, gid });
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
import { pathFromURL } from "../../util.ts";
|
||||
|
||||
export function copyFileSync(
|
||||
fromPath: string | URL,
|
||||
toPath: string | URL,
|
||||
): void {
|
||||
sendSync("op_copy_file", {
|
||||
from: pathFromURL(fromPath),
|
||||
to: pathFromURL(toPath),
|
||||
});
|
||||
}
|
||||
|
||||
export async function copyFile(
|
||||
fromPath: string | URL,
|
||||
toPath: string | URL,
|
||||
): Promise<void> {
|
||||
await sendAsync("op_copy_file", {
|
||||
from: pathFromURL(fromPath),
|
||||
to: pathFromURL(toPath),
|
||||
});
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync } from "../dispatch_json.ts";
|
||||
|
||||
export function cwd(): string {
|
||||
return sendSync("op_cwd");
|
||||
}
|
||||
|
||||
export function chdir(directory: string): void {
|
||||
sendSync("op_chdir", { directory });
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
|
||||
export function linkSync(oldpath: string, newpath: string): void {
|
||||
sendSync("op_link", { oldpath, newpath });
|
||||
}
|
||||
|
||||
export async function link(oldpath: string, newpath: string): Promise<void> {
|
||||
await sendAsync("op_link", { oldpath, newpath });
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
|
||||
export interface MakeTempOptions {
|
||||
dir?: string;
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
}
|
||||
|
||||
export function makeTempDirSync(options: MakeTempOptions = {}): string {
|
||||
return sendSync("op_make_temp_dir", options);
|
||||
}
|
||||
|
||||
export function makeTempDir(options: MakeTempOptions = {}): Promise<string> {
|
||||
return sendAsync("op_make_temp_dir", options);
|
||||
}
|
||||
|
||||
export function makeTempFileSync(options: MakeTempOptions = {}): string {
|
||||
return sendSync("op_make_temp_file", options);
|
||||
}
|
||||
|
||||
export function makeTempFile(options: MakeTempOptions = {}): Promise<string> {
|
||||
return sendAsync("op_make_temp_file", options);
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
|
||||
export interface MkdirOptions {
|
||||
recursive?: boolean;
|
||||
mode?: number;
|
||||
}
|
||||
|
||||
interface MkdirArgs {
|
||||
path: string;
|
||||
recursive: boolean;
|
||||
mode?: number;
|
||||
}
|
||||
|
||||
function mkdirArgs(path: string, options?: MkdirOptions): MkdirArgs {
|
||||
const args: MkdirArgs = { path, recursive: false };
|
||||
if (options != null) {
|
||||
if (typeof options.recursive == "boolean") {
|
||||
args.recursive = options.recursive;
|
||||
}
|
||||
if (options.mode) {
|
||||
args.mode = options.mode;
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
export function mkdirSync(path: string, options?: MkdirOptions): void {
|
||||
sendSync("op_mkdir", mkdirArgs(path, options));
|
||||
}
|
||||
|
||||
export async function mkdir(
|
||||
path: string,
|
||||
options?: MkdirOptions,
|
||||
): Promise<void> {
|
||||
await sendAsync("op_mkdir", mkdirArgs(path, options));
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
import { pathFromURL } from "../../util.ts";
|
||||
|
||||
export interface OpenOptions {
|
||||
read?: boolean;
|
||||
write?: boolean;
|
||||
append?: boolean;
|
||||
truncate?: boolean;
|
||||
create?: boolean;
|
||||
createNew?: boolean;
|
||||
/** Permissions to use if creating the file (defaults to `0o666`, before
|
||||
* the process's umask).
|
||||
* It's an error to specify mode without also setting create or createNew to `true`.
|
||||
* Ignored on Windows. */
|
||||
mode?: number;
|
||||
}
|
||||
|
||||
export function openSync(path: string | URL, options: OpenOptions): number {
|
||||
const mode: number | undefined = options?.mode;
|
||||
return sendSync("op_open", { path: pathFromURL(path), options, mode });
|
||||
}
|
||||
|
||||
export function open(
|
||||
path: string | URL,
|
||||
options: OpenOptions,
|
||||
): Promise<number> {
|
||||
const mode: number | undefined = options?.mode;
|
||||
return sendAsync("op_open", { path: pathFromURL(path), options, mode });
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
import { pathFromURL } from "../../util.ts";
|
||||
|
||||
export interface DirEntry {
|
||||
name: string;
|
||||
isFile: boolean;
|
||||
isDirectory: boolean;
|
||||
isSymlink: boolean;
|
||||
}
|
||||
|
||||
interface ReadDirResponse {
|
||||
entries: DirEntry[];
|
||||
}
|
||||
|
||||
function res(response: ReadDirResponse): DirEntry[] {
|
||||
return response.entries;
|
||||
}
|
||||
|
||||
export function readDirSync(path: string | URL): Iterable<DirEntry> {
|
||||
return res(sendSync("op_read_dir", { path: pathFromURL(path) }))[
|
||||
Symbol.iterator
|
||||
]();
|
||||
}
|
||||
|
||||
export function readDir(path: string | URL): AsyncIterable<DirEntry> {
|
||||
const array = sendAsync("op_read_dir", { path: pathFromURL(path) }).then(res);
|
||||
return {
|
||||
async *[Symbol.asyncIterator](): AsyncIterableIterator<DirEntry> {
|
||||
yield* await array;
|
||||
},
|
||||
};
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
|
||||
export function readLinkSync(path: string): string {
|
||||
return sendSync("op_read_link", { path });
|
||||
}
|
||||
|
||||
export function readLink(path: string): Promise<string> {
|
||||
return sendAsync("op_read_link", { path });
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
|
||||
export function realPathSync(path: string): string {
|
||||
return sendSync("op_realpath", { path });
|
||||
}
|
||||
|
||||
export function realPath(path: string): Promise<string> {
|
||||
return sendAsync("op_realpath", { path });
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
import { pathFromURL } from "../../util.ts";
|
||||
|
||||
export interface RemoveOptions {
|
||||
recursive?: boolean;
|
||||
}
|
||||
|
||||
export function removeSync(
|
||||
path: string | URL,
|
||||
options: RemoveOptions = {},
|
||||
): void {
|
||||
sendSync("op_remove", {
|
||||
path: pathFromURL(path),
|
||||
recursive: !!options.recursive,
|
||||
});
|
||||
}
|
||||
|
||||
export async function remove(
|
||||
path: string | URL,
|
||||
options: RemoveOptions = {},
|
||||
): Promise<void> {
|
||||
await sendAsync("op_remove", {
|
||||
path: pathFromURL(path),
|
||||
recursive: !!options.recursive,
|
||||
});
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
|
||||
export function renameSync(oldpath: string, newpath: string): void {
|
||||
sendSync("op_rename", { oldpath, newpath });
|
||||
}
|
||||
|
||||
export async function rename(oldpath: string, newpath: string): Promise<void> {
|
||||
await sendAsync("op_rename", { oldpath, newpath });
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
import type { SeekMode } from "../../io.ts";
|
||||
|
||||
export function seekSync(
|
||||
rid: number,
|
||||
offset: number,
|
||||
whence: SeekMode,
|
||||
): number {
|
||||
return sendSync("op_seek", { rid, offset, whence });
|
||||
}
|
||||
|
||||
export function seek(
|
||||
rid: number,
|
||||
offset: number,
|
||||
whence: SeekMode,
|
||||
): Promise<number> {
|
||||
return sendAsync("op_seek", { rid, offset, whence });
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
import { build } from "../../build.ts";
|
||||
import { pathFromURL } from "../../util.ts";
|
||||
|
||||
export interface FileInfo {
|
||||
size: number;
|
||||
mtime: Date | null;
|
||||
atime: Date | null;
|
||||
birthtime: Date | null;
|
||||
dev: number | null;
|
||||
ino: number | null;
|
||||
mode: number | null;
|
||||
nlink: number | null;
|
||||
uid: number | null;
|
||||
gid: number | null;
|
||||
rdev: number | null;
|
||||
blksize: number | null;
|
||||
blocks: number | null;
|
||||
isFile: boolean;
|
||||
isDirectory: boolean;
|
||||
isSymlink: boolean;
|
||||
}
|
||||
|
||||
export interface StatResponse {
|
||||
isFile: boolean;
|
||||
isDirectory: boolean;
|
||||
isSymlink: boolean;
|
||||
size: number;
|
||||
mtime: number | null;
|
||||
atime: number | null;
|
||||
birthtime: number | null;
|
||||
// Unix only members
|
||||
dev: number;
|
||||
ino: number;
|
||||
mode: number;
|
||||
nlink: number;
|
||||
uid: number;
|
||||
gid: number;
|
||||
rdev: number;
|
||||
blksize: number;
|
||||
blocks: number;
|
||||
}
|
||||
|
||||
// @internal
|
||||
export function parseFileInfo(response: StatResponse): FileInfo {
|
||||
const unix = build.os === "darwin" || build.os === "linux";
|
||||
return {
|
||||
isFile: response.isFile,
|
||||
isDirectory: response.isDirectory,
|
||||
isSymlink: response.isSymlink,
|
||||
size: response.size,
|
||||
mtime: response.mtime != null ? new Date(response.mtime) : null,
|
||||
atime: response.atime != null ? new Date(response.atime) : null,
|
||||
birthtime: response.birthtime != null ? new Date(response.birthtime) : null,
|
||||
// Only non-null if on Unix
|
||||
dev: unix ? response.dev : null,
|
||||
ino: unix ? response.ino : null,
|
||||
mode: unix ? response.mode : null,
|
||||
nlink: unix ? response.nlink : null,
|
||||
uid: unix ? response.uid : null,
|
||||
gid: unix ? response.gid : null,
|
||||
rdev: unix ? response.rdev : null,
|
||||
blksize: unix ? response.blksize : null,
|
||||
blocks: unix ? response.blocks : null,
|
||||
};
|
||||
}
|
||||
|
||||
export function fstatSync(rid: number): FileInfo {
|
||||
return parseFileInfo(sendSync("op_fstat", { rid }));
|
||||
}
|
||||
|
||||
export async function fstat(rid: number): Promise<FileInfo> {
|
||||
return parseFileInfo(await sendAsync("op_fstat", { rid }));
|
||||
}
|
||||
|
||||
export async function lstat(path: string | URL): Promise<FileInfo> {
|
||||
const res = await sendAsync("op_stat", {
|
||||
path: pathFromURL(path),
|
||||
lstat: true,
|
||||
});
|
||||
return parseFileInfo(res);
|
||||
}
|
||||
|
||||
export function lstatSync(path: string | URL): FileInfo {
|
||||
const res = sendSync("op_stat", {
|
||||
path: pathFromURL(path),
|
||||
lstat: true,
|
||||
});
|
||||
return parseFileInfo(res);
|
||||
}
|
||||
|
||||
export async function stat(path: string | URL): Promise<FileInfo> {
|
||||
const res = await sendAsync("op_stat", {
|
||||
path: pathFromURL(path),
|
||||
lstat: false,
|
||||
});
|
||||
return parseFileInfo(res);
|
||||
}
|
||||
|
||||
export function statSync(path: string | URL): FileInfo {
|
||||
const res = sendSync("op_stat", {
|
||||
path: pathFromURL(path),
|
||||
lstat: false,
|
||||
});
|
||||
return parseFileInfo(res);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
|
||||
export interface SymlinkOptions {
|
||||
type: "file" | "dir";
|
||||
}
|
||||
|
||||
export function symlinkSync(
|
||||
oldpath: string,
|
||||
newpath: string,
|
||||
options?: SymlinkOptions,
|
||||
): void {
|
||||
sendSync("op_symlink", { oldpath, newpath, options });
|
||||
}
|
||||
|
||||
export async function symlink(
|
||||
oldpath: string,
|
||||
newpath: string,
|
||||
options?: SymlinkOptions,
|
||||
): Promise<void> {
|
||||
await sendAsync("op_symlink", { oldpath, newpath, options });
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
|
||||
export function fdatasyncSync(rid: number): void {
|
||||
sendSync("op_fdatasync", { rid });
|
||||
}
|
||||
|
||||
export async function fdatasync(rid: number): Promise<void> {
|
||||
await sendAsync("op_fdatasync", { rid });
|
||||
}
|
||||
|
||||
export function fsyncSync(rid: number): void {
|
||||
sendSync("op_fsync", { rid });
|
||||
}
|
||||
|
||||
export async function fsync(rid: number): Promise<void> {
|
||||
await sendAsync("op_fsync", { rid });
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
|
||||
function coerceLen(len?: number): number {
|
||||
if (len == null || len < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
export function ftruncateSync(rid: number, len?: number): void {
|
||||
sendSync("op_ftruncate", { rid, len: coerceLen(len) });
|
||||
}
|
||||
|
||||
export async function ftruncate(rid: number, len?: number): Promise<void> {
|
||||
await sendAsync("op_ftruncate", { rid, len: coerceLen(len) });
|
||||
}
|
||||
|
||||
export function truncateSync(path: string, len?: number): void {
|
||||
sendSync("op_truncate", { path, len: coerceLen(len) });
|
||||
}
|
||||
|
||||
export async function truncate(path: string, len?: number): Promise<void> {
|
||||
await sendAsync("op_truncate", { path, len: coerceLen(len) });
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync } from "../dispatch_json.ts";
|
||||
|
||||
export function umask(mask?: number): number {
|
||||
return sendSync("op_umask", { mask });
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
||||
|
||||
function toSecondsFromEpoch(v: number | Date): number {
|
||||
return v instanceof Date ? Math.trunc(v.valueOf() / 1000) : v;
|
||||
}
|
||||
|
||||
export function utimeSync(
|
||||
path: string,
|
||||
atime: number | Date,
|
||||
mtime: number | Date,
|
||||
): void {
|
||||
sendSync("op_utime", {
|
||||
path,
|
||||
// TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple
|
||||
atime: toSecondsFromEpoch(atime),
|
||||
mtime: toSecondsFromEpoch(mtime),
|
||||
});
|
||||
}
|
||||
|
||||
export async function utime(
|
||||
path: string,
|
||||
atime: number | Date,
|
||||
mtime: number | Date,
|
||||
): Promise<void> {
|
||||
await sendAsync("op_utime", {
|
||||
path,
|
||||
// TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple
|
||||
atime: toSecondsFromEpoch(atime),
|
||||
mtime: toSecondsFromEpoch(mtime),
|
||||
});
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "./dispatch_json.ts";
|
||||
import { close } from "./resources.ts";
|
||||
|
||||
export interface FsEvent {
|
||||
kind: "any" | "access" | "create" | "modify" | "remove";
|
||||
paths: string[];
|
||||
}
|
||||
|
||||
interface FsWatcherOptions {
|
||||
recursive: boolean;
|
||||
}
|
||||
|
||||
class FsWatcher implements AsyncIterableIterator<FsEvent> {
|
||||
readonly rid: number;
|
||||
|
||||
constructor(paths: string[], options: FsWatcherOptions) {
|
||||
const { recursive } = options;
|
||||
this.rid = sendSync("op_fs_events_open", { recursive, paths });
|
||||
}
|
||||
|
||||
next(): Promise<IteratorResult<FsEvent>> {
|
||||
return sendAsync("op_fs_events_poll", {
|
||||
rid: this.rid,
|
||||
});
|
||||
}
|
||||
|
||||
return(value?: FsEvent): Promise<IteratorResult<FsEvent>> {
|
||||
close(this.rid);
|
||||
return Promise.resolve({ value, done: true });
|
||||
}
|
||||
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<FsEvent> {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function watchFs(
|
||||
paths: string | string[],
|
||||
options: FsWatcherOptions = { recursive: true },
|
||||
): AsyncIterableIterator<FsEvent> {
|
||||
return new FsWatcher(Array.isArray(paths) ? paths : [paths], options);
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync } from "./dispatch_json.ts";
|
||||
import { assert } from "../util.ts";
|
||||
|
||||
export function getRandomValues<
|
||||
T extends
|
||||
| Int8Array
|
||||
| Uint8Array
|
||||
| Uint8ClampedArray
|
||||
| Int16Array
|
||||
| Uint16Array
|
||||
| Int32Array
|
||||
| Uint32Array,
|
||||
>(typedArray: T): T {
|
||||
assert(typedArray !== null, "Input must not be null");
|
||||
assert(typedArray.length <= 65536, "Input must not be longer than 65536");
|
||||
const ui8 = new Uint8Array(
|
||||
typedArray.buffer,
|
||||
typedArray.byteOffset,
|
||||
typedArray.byteLength,
|
||||
);
|
||||
sendSync("op_get_random_values", {}, ui8);
|
||||
return typedArray;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
/** https://url.spec.whatwg.org/#idna */
|
||||
|
||||
import { sendSync } from "./dispatch_json.ts";
|
||||
|
||||
export function domainToAscii(
|
||||
domain: string,
|
||||
{ beStrict = false }: { beStrict?: boolean } = {},
|
||||
): string {
|
||||
return sendSync("op_domain_to_ascii", { domain, beStrict });
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendAsyncMinimal, sendSyncMinimal } from "./dispatch_minimal.ts";
|
||||
|
||||
export function readSync(rid: number, buffer: Uint8Array): number | null {
|
||||
if (buffer.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const nread = sendSyncMinimal("op_read", rid, buffer);
|
||||
if (nread < 0) {
|
||||
throw new Error("read error");
|
||||
}
|
||||
|
||||
return nread === 0 ? null : nread;
|
||||
}
|
||||
|
||||
export async function read(
|
||||
rid: number,
|
||||
buffer: Uint8Array,
|
||||
): Promise<number | null> {
|
||||
if (buffer.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const nread = await sendAsyncMinimal("op_read", rid, buffer);
|
||||
if (nread < 0) {
|
||||
throw new Error("read error");
|
||||
}
|
||||
|
||||
return nread === 0 ? null : nread;
|
||||
}
|
||||
|
||||
export function writeSync(rid: number, data: Uint8Array): number {
|
||||
const result = sendSyncMinimal("op_write", rid, data);
|
||||
if (result < 0) {
|
||||
throw new Error("write error");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function write(rid: number, data: Uint8Array): Promise<number> {
|
||||
const result = await sendAsyncMinimal("op_write", rid, data);
|
||||
if (result < 0) {
|
||||
throw new Error("write error");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "./dispatch_json.ts";
|
||||
|
||||
export interface NetAddr {
|
||||
transport: "tcp" | "udp";
|
||||
hostname: string;
|
||||
port: number;
|
||||
}
|
||||
|
||||
export interface UnixAddr {
|
||||
transport: "unix" | "unixpacket";
|
||||
path: string;
|
||||
}
|
||||
|
||||
export type Addr = NetAddr | UnixAddr;
|
||||
|
||||
export enum ShutdownMode {
|
||||
// See http://man7.org/linux/man-pages/man2/shutdown.2.html
|
||||
// Corresponding to SHUT_RD, SHUT_WR, SHUT_RDWR
|
||||
Read = 0,
|
||||
Write = 1,
|
||||
ReadWrite, // unused
|
||||
}
|
||||
|
||||
export function shutdown(rid: number, how: ShutdownMode): Promise<void> {
|
||||
sendSync("op_shutdown", { rid, how });
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
interface AcceptResponse {
|
||||
rid: number;
|
||||
localAddr: Addr;
|
||||
remoteAddr: Addr;
|
||||
}
|
||||
|
||||
export function accept(
|
||||
rid: number,
|
||||
transport: string,
|
||||
): Promise<AcceptResponse> {
|
||||
return sendAsync("op_accept", { rid, transport });
|
||||
}
|
||||
|
||||
export type ListenRequest = Addr;
|
||||
|
||||
interface ListenResponse {
|
||||
rid: number;
|
||||
localAddr: Addr;
|
||||
}
|
||||
|
||||
export function listen(args: ListenRequest): ListenResponse {
|
||||
return sendSync("op_listen", args);
|
||||
}
|
||||
|
||||
interface ConnectResponse {
|
||||
rid: number;
|
||||
localAddr: Addr;
|
||||
remoteAddr: Addr;
|
||||
}
|
||||
|
||||
export type ConnectRequest = Addr;
|
||||
|
||||
export function connect(args: ConnectRequest): Promise<ConnectResponse> {
|
||||
return sendAsync("op_connect", args);
|
||||
}
|
||||
|
||||
interface ReceiveResponse {
|
||||
size: number;
|
||||
remoteAddr: Addr;
|
||||
}
|
||||
|
||||
export function receive(
|
||||
rid: number,
|
||||
transport: string,
|
||||
zeroCopy: Uint8Array,
|
||||
): Promise<ReceiveResponse> {
|
||||
return sendAsync("op_datagram_receive", { rid, transport }, zeroCopy);
|
||||
}
|
||||
|
||||
export type SendRequest = {
|
||||
rid: number;
|
||||
} & Addr;
|
||||
|
||||
export function send(args: SendRequest, zeroCopy: Uint8Array): Promise<number> {
|
||||
return sendAsync("op_datagram_send", args, zeroCopy);
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync } from "./dispatch_json.ts";
|
||||
|
||||
export function loadavg(): number[] {
|
||||
return sendSync("op_loadavg");
|
||||
}
|
||||
|
||||
export function hostname(): string {
|
||||
return sendSync("op_hostname");
|
||||
}
|
||||
|
||||
export function osRelease(): string {
|
||||
return sendSync("op_os_release");
|
||||
}
|
||||
|
||||
export function exit(code = 0): never {
|
||||
sendSync("op_exit", { code });
|
||||
throw new Error("Code not reachable");
|
||||
}
|
||||
|
||||
function setEnv(key: string, value: string): void {
|
||||
sendSync("op_set_env", { key, value });
|
||||
}
|
||||
|
||||
function getEnv(key: string): string | undefined {
|
||||
return sendSync("op_get_env", { key })[0];
|
||||
}
|
||||
|
||||
function deleteEnv(key: string): void {
|
||||
sendSync("op_delete_env", { key });
|
||||
}
|
||||
|
||||
export const env = {
|
||||
get: getEnv,
|
||||
toObject(): Record<string, string> {
|
||||
return sendSync("op_env");
|
||||
},
|
||||
set: setEnv,
|
||||
delete: deleteEnv,
|
||||
};
|
||||
|
||||
export function execPath(): string {
|
||||
return sendSync("op_exec_path");
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync } from "./dispatch_json.ts";
|
||||
import type { PermissionState } from "../permissions.ts";
|
||||
|
||||
interface PermissionRequest {
|
||||
name: string;
|
||||
url?: string;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export function query(desc: PermissionRequest): PermissionState {
|
||||
return sendSync("op_query_permission", desc).state;
|
||||
}
|
||||
|
||||
export function revoke(desc: PermissionRequest): PermissionState {
|
||||
return sendSync("op_revoke_permission", desc).state;
|
||||
}
|
||||
|
||||
export function request(desc: PermissionRequest): PermissionState {
|
||||
return sendSync("op_request_permission", desc).state;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync } from "./dispatch_json.ts";
|
||||
|
||||
export function openPlugin(filename: string): number {
|
||||
return sendSync("op_open_plugin", { filename });
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "./dispatch_json.ts";
|
||||
import { assert } from "../util.ts";
|
||||
|
||||
export function kill(pid: number, signo: number): void {
|
||||
sendSync("op_kill", { pid, signo });
|
||||
}
|
||||
|
||||
interface RunStatusResponse {
|
||||
gotSignal: boolean;
|
||||
exitCode: number;
|
||||
exitSignal: number;
|
||||
}
|
||||
|
||||
export function runStatus(rid: number): Promise<RunStatusResponse> {
|
||||
return sendAsync("op_run_status", { rid });
|
||||
}
|
||||
|
||||
interface RunRequest {
|
||||
cmd: string[];
|
||||
cwd?: string;
|
||||
env?: Array<[string, string]>;
|
||||
stdin: string;
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
stdinRid: number;
|
||||
stdoutRid: number;
|
||||
stderrRid: number;
|
||||
}
|
||||
|
||||
interface RunResponse {
|
||||
rid: number;
|
||||
pid: number;
|
||||
stdinRid: number | null;
|
||||
stdoutRid: number | null;
|
||||
stderrRid: number | null;
|
||||
}
|
||||
|
||||
export function run(request: RunRequest): RunResponse {
|
||||
assert(request.cmd.length > 0);
|
||||
return sendSync("op_run", request);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "./dispatch_json.ts";
|
||||
|
||||
export function startRepl(historyFile: string): number {
|
||||
return sendSync("op_repl_start", { historyFile });
|
||||
}
|
||||
|
||||
export function readline(rid: number, prompt: string): Promise<string> {
|
||||
return sendAsync("op_repl_readline", { rid, prompt });
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync } from "./dispatch_json.ts";
|
||||
|
||||
export type ResourceMap = Record<number, string>;
|
||||
|
||||
export function resources(): ResourceMap {
|
||||
const res = sendSync("op_resources") as Array<[number, string]>;
|
||||
const resources: ResourceMap = {};
|
||||
for (const resourceTuple of res) {
|
||||
resources[resourceTuple[0]] = resourceTuple[1];
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
export function close(rid: number): void {
|
||||
sendSync("op_close", { rid });
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync } from "./dispatch_json.ts";
|
||||
|
||||
export interface Start {
|
||||
args: string[];
|
||||
cwd: string;
|
||||
debugFlag: boolean;
|
||||
denoVersion: string;
|
||||
noColor: boolean;
|
||||
pid: number;
|
||||
ppid: number;
|
||||
repl: boolean;
|
||||
target: string;
|
||||
tsVersion: string;
|
||||
unstableFlag: boolean;
|
||||
v8Version: string;
|
||||
versionFlag: boolean;
|
||||
}
|
||||
|
||||
export function opStart(): Start {
|
||||
return sendSync("op_start");
|
||||
}
|
||||
|
||||
export function opMainModule(): string {
|
||||
return sendSync("op_main_module");
|
||||
}
|
||||
|
||||
export interface Metrics {
|
||||
opsDispatched: number;
|
||||
opsDispatchedSync: number;
|
||||
opsDispatchedAsync: number;
|
||||
opsDispatchedAsyncUnref: number;
|
||||
opsCompleted: number;
|
||||
opsCompletedSync: number;
|
||||
opsCompletedAsync: number;
|
||||
opsCompletedAsyncUnref: number;
|
||||
bytesSentControl: number;
|
||||
bytesSentData: number;
|
||||
bytesReceived: number;
|
||||
}
|
||||
|
||||
export function metrics(): Metrics {
|
||||
return sendSync("op_metrics");
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendAsync } from "./dispatch_json.ts";
|
||||
import type { DiagnosticItem } from "../diagnostics.ts";
|
||||
|
||||
interface CompileRequest {
|
||||
rootName: string;
|
||||
sources?: Record<string, string>;
|
||||
options?: string;
|
||||
bundle: boolean;
|
||||
}
|
||||
|
||||
interface CompileResponse {
|
||||
diagnostics: DiagnosticItem[];
|
||||
output?: string;
|
||||
emitMap?: Record<string, Record<string, string>>;
|
||||
}
|
||||
|
||||
export function compile(request: CompileRequest): Promise<CompileResponse> {
|
||||
return sendAsync("op_compile", request);
|
||||
}
|
||||
|
||||
interface TranspileRequest {
|
||||
sources: Record<string, string>;
|
||||
options?: string;
|
||||
}
|
||||
|
||||
export interface TranspileOnlyResult {
|
||||
source: string;
|
||||
map?: string;
|
||||
}
|
||||
|
||||
export function transpile(
|
||||
request: TranspileRequest,
|
||||
): Promise<Record<string, TranspileOnlyResult>> {
|
||||
return sendAsync("op_transpile", request);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "./dispatch_json.ts";
|
||||
|
||||
interface BindSignalResponse {
|
||||
rid: number;
|
||||
}
|
||||
|
||||
interface PollSignalResponse {
|
||||
done: boolean;
|
||||
}
|
||||
|
||||
export function bindSignal(signo: number): BindSignalResponse {
|
||||
return sendSync("op_signal_bind", { signo });
|
||||
}
|
||||
|
||||
export function pollSignal(rid: number): Promise<PollSignalResponse> {
|
||||
return sendAsync("op_signal_poll", { rid });
|
||||
}
|
||||
|
||||
export function unbindSignal(rid: number): void {
|
||||
sendSync("op_signal_unbind", { rid });
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync, sendAsync } from "./dispatch_json.ts";
|
||||
|
||||
interface NowResponse {
|
||||
seconds: number;
|
||||
subsecNanos: number;
|
||||
}
|
||||
|
||||
export function stopGlobalTimer(): void {
|
||||
sendSync("op_global_timer_stop");
|
||||
}
|
||||
|
||||
export async function startGlobalTimer(timeout: number): Promise<void> {
|
||||
await sendAsync("op_global_timer", { timeout });
|
||||
}
|
||||
|
||||
export function now(): NowResponse {
|
||||
return sendSync("op_now");
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendAsync, sendSync } from "./dispatch_json.ts";
|
||||
|
||||
export interface ConnectTLSRequest {
|
||||
transport: "tcp";
|
||||
hostname: string;
|
||||
port: number;
|
||||
certFile?: string;
|
||||
}
|
||||
|
||||
interface EstablishTLSResponse {
|
||||
rid: number;
|
||||
localAddr: {
|
||||
hostname: string;
|
||||
port: number;
|
||||
transport: "tcp";
|
||||
};
|
||||
remoteAddr: {
|
||||
hostname: string;
|
||||
port: number;
|
||||
transport: "tcp";
|
||||
};
|
||||
}
|
||||
|
||||
export function connectTls(
|
||||
args: ConnectTLSRequest,
|
||||
): Promise<EstablishTLSResponse> {
|
||||
return sendAsync("op_connect_tls", args);
|
||||
}
|
||||
|
||||
interface AcceptTLSResponse {
|
||||
rid: number;
|
||||
localAddr: {
|
||||
hostname: string;
|
||||
port: number;
|
||||
transport: "tcp";
|
||||
};
|
||||
remoteAddr: {
|
||||
hostname: string;
|
||||
port: number;
|
||||
transport: "tcp";
|
||||
};
|
||||
}
|
||||
|
||||
export function acceptTLS(rid: number): Promise<AcceptTLSResponse> {
|
||||
return sendAsync("op_accept_tls", { rid });
|
||||
}
|
||||
|
||||
export interface ListenTLSRequest {
|
||||
port: number;
|
||||
hostname: string;
|
||||
transport: "tcp";
|
||||
certFile: string;
|
||||
keyFile: string;
|
||||
}
|
||||
|
||||
interface ListenTLSResponse {
|
||||
rid: number;
|
||||
localAddr: {
|
||||
hostname: string;
|
||||
port: number;
|
||||
transport: "tcp";
|
||||
};
|
||||
}
|
||||
|
||||
export function listenTls(args: ListenTLSRequest): ListenTLSResponse {
|
||||
return sendSync("op_listen_tls", args);
|
||||
}
|
||||
|
||||
export interface StartTLSRequest {
|
||||
rid: number;
|
||||
hostname: string;
|
||||
certFile?: string;
|
||||
}
|
||||
|
||||
export function startTls(args: StartTLSRequest): Promise<EstablishTLSResponse> {
|
||||
return sendAsync("op_start_tls", args);
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync } from "./dispatch_json.ts";
|
||||
|
||||
export function consoleSize(rid: number): [number, number] {
|
||||
return sendSync("op_console_size", { rid });
|
||||
}
|
||||
|
||||
export function isatty(rid: number): boolean {
|
||||
return sendSync("op_isatty", { rid });
|
||||
}
|
||||
|
||||
export function setRaw(rid: number, mode: boolean): void {
|
||||
sendSync("op_set_raw", { rid, mode });
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { sendSync } from "./dispatch_json.ts";
|
||||
|
||||
export function postMessage(data: Uint8Array): void {
|
||||
sendSync("op_worker_post_message", {}, data);
|
||||
}
|
||||
|
||||
export function close(): void {
|
||||
sendSync("op_worker_close");
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { sendAsync, sendSync } from "./dispatch_json.ts";
|
||||
|
||||
interface CreateWorkerResponse {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export function createWorker(
|
||||
specifier: string,
|
||||
hasSourceCode: boolean,
|
||||
sourceCode: string,
|
||||
useDenoNamespace: boolean,
|
||||
name?: string,
|
||||
): CreateWorkerResponse {
|
||||
return sendSync("op_create_worker", {
|
||||
specifier,
|
||||
hasSourceCode,
|
||||
sourceCode,
|
||||
name,
|
||||
useDenoNamespace,
|
||||
});
|
||||
}
|
||||
|
||||
export function hostTerminateWorker(id: number): void {
|
||||
sendSync("op_host_terminate_worker", { id });
|
||||
}
|
||||
|
||||
export function hostPostMessage(id: number, data: Uint8Array): void {
|
||||
sendSync("op_host_post_message", { id }, data);
|
||||
}
|
||||
|
||||
export function hostGetMessage(id: number): Promise<any> {
|
||||
return sendAsync("op_host_get_message", { id });
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import * as permissionsOps from "./ops/permissions.ts";
|
||||
|
||||
export type PermissionName =
|
||||
| "read"
|
||||
| "write"
|
||||
| "net"
|
||||
| "env"
|
||||
| "run"
|
||||
| "plugin"
|
||||
| "hrtime";
|
||||
// NOTE: Keep in sync with cli/permissions.rs
|
||||
|
||||
export type PermissionState = "granted" | "denied" | "prompt";
|
||||
|
||||
export interface RunPermissionDescriptor {
|
||||
name: "run";
|
||||
}
|
||||
|
||||
export interface ReadPermissionDescriptor {
|
||||
name: "read";
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export interface WritePermissionDescriptor {
|
||||
name: "write";
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export interface NetPermissionDescriptor {
|
||||
name: "net";
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface EnvPermissionDescriptor {
|
||||
name: "env";
|
||||
}
|
||||
|
||||
export interface PluginPermissionDescriptor {
|
||||
name: "plugin";
|
||||
}
|
||||
|
||||
export interface HrtimePermissionDescriptor {
|
||||
name: "hrtime";
|
||||
}
|
||||
|
||||
export type PermissionDescriptor =
|
||||
| RunPermissionDescriptor
|
||||
| ReadPermissionDescriptor
|
||||
| WritePermissionDescriptor
|
||||
| NetPermissionDescriptor
|
||||
| EnvPermissionDescriptor
|
||||
| PluginPermissionDescriptor
|
||||
| HrtimePermissionDescriptor;
|
||||
|
||||
export class PermissionStatus {
|
||||
constructor(public state: PermissionState) {}
|
||||
// TODO(kt3k): implement onchange handler
|
||||
}
|
||||
|
||||
export class Permissions {
|
||||
query(desc: PermissionDescriptor): Promise<PermissionStatus> {
|
||||
const state = permissionsOps.query(desc);
|
||||
return Promise.resolve(new PermissionStatus(state));
|
||||
}
|
||||
|
||||
revoke(desc: PermissionDescriptor): Promise<PermissionStatus> {
|
||||
const state = permissionsOps.revoke(desc);
|
||||
return Promise.resolve(new PermissionStatus(state));
|
||||
}
|
||||
|
||||
request(desc: PermissionDescriptor): Promise<PermissionStatus> {
|
||||
const state = permissionsOps.request(desc);
|
||||
return Promise.resolve(new PermissionStatus(state));
|
||||
}
|
||||
}
|
||||
|
||||
export const permissions = new Permissions();
|
|
@ -1,136 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { File } from "./files.ts";
|
||||
import { close } from "./ops/resources.ts";
|
||||
import type { Closer, Reader, Writer } from "./io.ts";
|
||||
import { readAll } from "./buffer.ts";
|
||||
import { kill, runStatus as runStatusOp, run as runOp } from "./ops/process.ts";
|
||||
|
||||
// TODO Maybe extend VSCode's 'CommandOptions'?
|
||||
// See https://code.visualstudio.com/docs/editor/tasks-appendix#_schema-for-tasksjson
|
||||
export interface RunOptions {
|
||||
cmd: string[];
|
||||
cwd?: string;
|
||||
env?: Record<string, string>;
|
||||
stdout?: "inherit" | "piped" | "null" | number;
|
||||
stderr?: "inherit" | "piped" | "null" | number;
|
||||
stdin?: "inherit" | "piped" | "null" | number;
|
||||
}
|
||||
|
||||
async function runStatus(rid: number): Promise<ProcessStatus> {
|
||||
const res = await runStatusOp(rid);
|
||||
|
||||
if (res.gotSignal) {
|
||||
const signal = res.exitSignal;
|
||||
return { success: false, code: 128 + signal, signal };
|
||||
} else if (res.exitCode != 0) {
|
||||
return { success: false, code: res.exitCode };
|
||||
} else {
|
||||
return { success: true, code: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
export class Process<T extends RunOptions = RunOptions> {
|
||||
readonly rid: number;
|
||||
readonly pid: number;
|
||||
readonly stdin!: T["stdin"] extends "piped" ? Writer & Closer
|
||||
: (Writer & Closer) | null;
|
||||
readonly stdout!: T["stdout"] extends "piped" ? Reader & Closer
|
||||
: (Reader & Closer) | null;
|
||||
readonly stderr!: T["stderr"] extends "piped" ? Reader & Closer
|
||||
: (Reader & Closer) | null;
|
||||
|
||||
// @internal
|
||||
constructor(res: RunResponse) {
|
||||
this.rid = res.rid;
|
||||
this.pid = res.pid;
|
||||
|
||||
if (res.stdinRid && res.stdinRid > 0) {
|
||||
this.stdin = (new File(res.stdinRid) as unknown) as Process<T>["stdin"];
|
||||
}
|
||||
|
||||
if (res.stdoutRid && res.stdoutRid > 0) {
|
||||
this.stdout = (new File(res.stdoutRid) as unknown) as Process<
|
||||
T
|
||||
>["stdout"];
|
||||
}
|
||||
|
||||
if (res.stderrRid && res.stderrRid > 0) {
|
||||
this.stderr = (new File(res.stderrRid) as unknown) as Process<
|
||||
T
|
||||
>["stderr"];
|
||||
}
|
||||
}
|
||||
|
||||
status(): Promise<ProcessStatus> {
|
||||
return runStatus(this.rid);
|
||||
}
|
||||
|
||||
async output(): Promise<Uint8Array> {
|
||||
if (!this.stdout) {
|
||||
throw new TypeError("stdout was not piped");
|
||||
}
|
||||
try {
|
||||
return await readAll(this.stdout as Reader & Closer);
|
||||
} finally {
|
||||
(this.stdout as Reader & Closer).close();
|
||||
}
|
||||
}
|
||||
|
||||
async stderrOutput(): Promise<Uint8Array> {
|
||||
if (!this.stderr) {
|
||||
throw new TypeError("stderr was not piped");
|
||||
}
|
||||
try {
|
||||
return await readAll(this.stderr as Reader & Closer);
|
||||
} finally {
|
||||
(this.stderr as Reader & Closer).close();
|
||||
}
|
||||
}
|
||||
|
||||
close(): void {
|
||||
close(this.rid);
|
||||
}
|
||||
|
||||
kill(signo: number): void {
|
||||
kill(this.pid, signo);
|
||||
}
|
||||
}
|
||||
|
||||
export type ProcessStatus =
|
||||
| { success: true; code: 0; signal?: undefined }
|
||||
| { success: false; code: number; signal?: number };
|
||||
|
||||
function isRid(arg: unknown): arg is number {
|
||||
return !isNaN(arg as number);
|
||||
}
|
||||
|
||||
interface RunResponse {
|
||||
rid: number;
|
||||
pid: number;
|
||||
stdinRid: number | null;
|
||||
stdoutRid: number | null;
|
||||
stderrRid: number | null;
|
||||
}
|
||||
|
||||
export function run<T extends RunOptions = RunOptions>({
|
||||
cmd,
|
||||
cwd = undefined,
|
||||
env = {},
|
||||
stdout = "inherit",
|
||||
stderr = "inherit",
|
||||
stdin = "inherit",
|
||||
}: T): Process<T> {
|
||||
const res = runOp({
|
||||
cmd: cmd.map(String),
|
||||
cwd,
|
||||
env: Object.entries(env),
|
||||
stdin: isRid(stdin) ? "" : stdin,
|
||||
stdout: isRid(stdout) ? "" : stdout,
|
||||
stderr: isRid(stderr) ? "" : stderr,
|
||||
stdinRid: isRid(stdin) ? stdin : 0,
|
||||
stdoutRid: isRid(stdout) ? stdout : 0,
|
||||
stderrRid: isRid(stderr) ? stderr : 0,
|
||||
}) as RunResponse;
|
||||
return new Process<T>(res);
|
||||
}
|
252
cli/js/rbtree.ts
252
cli/js/rbtree.ts
|
@ -1,252 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// Derived from https://github.com/vadimg/js_bintrees. MIT Licensed.
|
||||
|
||||
import { assert } from "./util.ts";
|
||||
|
||||
class RBNode<T> {
|
||||
public left: this | null;
|
||||
public right: this | null;
|
||||
public red: boolean;
|
||||
|
||||
constructor(public data: T) {
|
||||
this.left = null;
|
||||
this.right = null;
|
||||
this.red = true;
|
||||
}
|
||||
|
||||
getChild(dir: boolean | number): this | null {
|
||||
return dir ? this.right : this.left;
|
||||
}
|
||||
|
||||
setChild(dir: boolean | number, val: this | null): void {
|
||||
if (dir) {
|
||||
this.right = val;
|
||||
} else {
|
||||
this.left = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class RBTree<T> {
|
||||
readonly #comparator: (a: T, b: T) => number;
|
||||
#root: RBNode<T> | null;
|
||||
|
||||
constructor(comparator: (a: T, b: T) => number) {
|
||||
this.#comparator = comparator;
|
||||
this.#root = null;
|
||||
}
|
||||
|
||||
/** Returns `null` if tree is empty. */
|
||||
min(): T | null {
|
||||
let res = this.#root;
|
||||
if (res === null) {
|
||||
return null;
|
||||
}
|
||||
while (res.left !== null) {
|
||||
res = res.left;
|
||||
}
|
||||
return res.data;
|
||||
}
|
||||
|
||||
/** Returns node `data` if found, `null` otherwise. */
|
||||
find(data: T): T | null {
|
||||
let res = this.#root;
|
||||
while (res !== null) {
|
||||
const c = this.#comparator(data, res.data);
|
||||
if (c === 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
res = res.getChild(c > 0);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** returns `true` if inserted, `false` if duplicate. */
|
||||
insert(data: T): boolean {
|
||||
let ret = false;
|
||||
|
||||
if (this.#root === null) {
|
||||
// empty tree
|
||||
this.#root = new RBNode(data);
|
||||
ret = true;
|
||||
} else {
|
||||
const head = new RBNode((null as unknown) as T); // fake tree root
|
||||
|
||||
let dir = 0;
|
||||
let last = 0;
|
||||
|
||||
// setup
|
||||
let gp = null; // grandparent
|
||||
let ggp = head; // grand-grand-parent
|
||||
let p: RBNode<T> | null = null; // parent
|
||||
let node: RBNode<T> | null = this.#root;
|
||||
ggp.right = this.#root;
|
||||
|
||||
// search down
|
||||
while (true) {
|
||||
if (node === null) {
|
||||
// insert new node at the bottom
|
||||
node = new RBNode(data);
|
||||
p!.setChild(dir, node);
|
||||
ret = true;
|
||||
} else if (isRed(node.left) && isRed(node.right)) {
|
||||
// color flip
|
||||
node.red = true;
|
||||
node.left!.red = false;
|
||||
node.right!.red = false;
|
||||
}
|
||||
|
||||
// fix red violation
|
||||
if (isRed(node) && isRed(p)) {
|
||||
const dir2 = ggp.right === gp;
|
||||
|
||||
assert(gp);
|
||||
if (node === p!.getChild(last)) {
|
||||
ggp.setChild(dir2, singleRotate(gp, !last));
|
||||
} else {
|
||||
ggp.setChild(dir2, doubleRotate(gp, !last));
|
||||
}
|
||||
}
|
||||
|
||||
const cmp = this.#comparator(node.data, data);
|
||||
|
||||
// stop if found
|
||||
if (cmp === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
last = dir;
|
||||
dir = Number(cmp < 0); // Fix type
|
||||
|
||||
// update helpers
|
||||
if (gp !== null) {
|
||||
ggp = gp;
|
||||
}
|
||||
gp = p;
|
||||
p = node;
|
||||
node = node.getChild(dir);
|
||||
}
|
||||
|
||||
// update root
|
||||
this.#root = head.right;
|
||||
}
|
||||
|
||||
// make root black
|
||||
this.#root!.red = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Returns `true` if removed, `false` if not found. */
|
||||
remove(data: T): boolean {
|
||||
if (this.#root === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const head = new RBNode((null as unknown) as T); // fake tree root
|
||||
let node = head;
|
||||
node.right = this.#root;
|
||||
let p = null; // parent
|
||||
let gp = null; // grand parent
|
||||
let found = null; // found item
|
||||
let dir: boolean | number = 1;
|
||||
|
||||
while (node.getChild(dir) !== null) {
|
||||
const last = dir;
|
||||
|
||||
// update helpers
|
||||
gp = p;
|
||||
p = node;
|
||||
node = node.getChild(dir)!;
|
||||
|
||||
const cmp = this.#comparator(data, node.data);
|
||||
|
||||
dir = cmp > 0;
|
||||
|
||||
// save found node
|
||||
if (cmp === 0) {
|
||||
found = node;
|
||||
}
|
||||
|
||||
// push the red node down
|
||||
if (!isRed(node) && !isRed(node.getChild(dir))) {
|
||||
if (isRed(node.getChild(!dir))) {
|
||||
const sr = singleRotate(node, dir);
|
||||
p.setChild(last, sr);
|
||||
p = sr;
|
||||
} else if (!isRed(node.getChild(!dir))) {
|
||||
const sibling = p.getChild(!last);
|
||||
if (sibling !== null) {
|
||||
if (
|
||||
!isRed(sibling.getChild(!last)) &&
|
||||
!isRed(sibling.getChild(last))
|
||||
) {
|
||||
// color flip
|
||||
p.red = false;
|
||||
sibling.red = true;
|
||||
node.red = true;
|
||||
} else {
|
||||
assert(gp);
|
||||
const dir2 = gp.right === p;
|
||||
|
||||
if (isRed(sibling.getChild(last))) {
|
||||
gp!.setChild(dir2, doubleRotate(p, last));
|
||||
} else if (isRed(sibling.getChild(!last))) {
|
||||
gp!.setChild(dir2, singleRotate(p, last));
|
||||
}
|
||||
|
||||
// ensure correct coloring
|
||||
const gpc = gp.getChild(dir2);
|
||||
assert(gpc);
|
||||
gpc.red = true;
|
||||
node.red = true;
|
||||
assert(gpc.left);
|
||||
gpc.left.red = false;
|
||||
assert(gpc.right);
|
||||
gpc.right.red = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// replace and remove if found
|
||||
if (found !== null) {
|
||||
found.data = node.data;
|
||||
assert(p);
|
||||
p.setChild(p.right === node, node.getChild(node.left === null));
|
||||
}
|
||||
|
||||
// update root and make it black
|
||||
this.#root = head.right;
|
||||
if (this.#root !== null) {
|
||||
this.#root.red = false;
|
||||
}
|
||||
|
||||
return found !== null;
|
||||
}
|
||||
}
|
||||
|
||||
function isRed<T>(node: RBNode<T> | null): boolean {
|
||||
return node !== null && node.red;
|
||||
}
|
||||
|
||||
function singleRotate<T>(root: RBNode<T>, dir: boolean | number): RBNode<T> {
|
||||
const save = root.getChild(!dir);
|
||||
assert(save);
|
||||
|
||||
root.setChild(!dir, save.getChild(dir));
|
||||
save.setChild(dir, root);
|
||||
|
||||
root.red = true;
|
||||
save.red = false;
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
function doubleRotate<T>(root: RBNode<T>, dir: boolean | number): RBNode<T> {
|
||||
root.setChild(!dir, singleRotate(root.getChild(!dir)!, !dir));
|
||||
return singleRotate(root, dir);
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { open, openSync } from "./files.ts";
|
||||
import { readAll, readAllSync } from "./buffer.ts";
|
||||
|
||||
export function readFileSync(path: string | URL): Uint8Array {
|
||||
const file = openSync(path);
|
||||
const contents = readAllSync(file);
|
||||
file.close();
|
||||
return contents;
|
||||
}
|
||||
|
||||
export async function readFile(path: string | URL): Promise<Uint8Array> {
|
||||
const file = await open(path);
|
||||
const contents = await readAll(file);
|
||||
file.close();
|
||||
return contents;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { open, openSync } from "./files.ts";
|
||||
import { readAll, readAllSync } from "./buffer.ts";
|
||||
|
||||
export function readTextFileSync(path: string | URL): string {
|
||||
const file = openSync(path);
|
||||
const contents = readAllSync(file);
|
||||
file.close();
|
||||
const decoder = new TextDecoder();
|
||||
return decoder.decode(contents);
|
||||
}
|
||||
|
||||
export async function readTextFile(path: string | URL): Promise<string> {
|
||||
const file = await open(path);
|
||||
const contents = await readAll(file);
|
||||
file.close();
|
||||
const decoder = new TextDecoder();
|
||||
return decoder.decode(contents);
|
||||
}
|
177
cli/js/repl.ts
177
cli/js/repl.ts
|
@ -1,177 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { exit } from "./ops/os.ts";
|
||||
import { core } from "./core.ts";
|
||||
import { version } from "./version.ts";
|
||||
import { inspectArgs } from "./web/console.ts";
|
||||
import { startRepl, readline } from "./ops/repl.ts";
|
||||
import { close } from "./ops/resources.ts";
|
||||
|
||||
function replLog(...args: unknown[]): void {
|
||||
core.print(inspectArgs(args) + "\n");
|
||||
}
|
||||
|
||||
function replError(...args: unknown[]): void {
|
||||
core.print(inspectArgs(args) + "\n", true);
|
||||
}
|
||||
|
||||
// Error messages that allow users to continue input
|
||||
// instead of throwing an error to REPL
|
||||
// ref: https://github.com/v8/v8/blob/master/src/message-template.h
|
||||
// TODO(kevinkassimo): this list might not be comprehensive
|
||||
const recoverableErrorMessages = [
|
||||
"Unexpected end of input", // { or [ or (
|
||||
"Missing initializer in const declaration", // const a
|
||||
"Missing catch or finally after try", // try {}
|
||||
"missing ) after argument list", // console.log(1
|
||||
"Unterminated template literal", // `template
|
||||
// TODO(kevinkassimo): need a parser to handling errors such as:
|
||||
// "Missing } in template expression" // `${ or `${ a 123 }`
|
||||
];
|
||||
|
||||
function isRecoverableError(e: Error): boolean {
|
||||
return recoverableErrorMessages.includes(e.message);
|
||||
}
|
||||
|
||||
// Returns `true` if `close()` is called in REPL.
|
||||
// We should quit the REPL when this function returns `true`.
|
||||
function isCloseCalled(): boolean {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (globalThis as any).closed;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type Value = any;
|
||||
|
||||
let lastEvalResult: Value = undefined;
|
||||
let lastThrownError: Value = undefined;
|
||||
|
||||
// Evaluate code.
|
||||
// Returns true if code is consumed (no error/irrecoverable error).
|
||||
// Returns false if error is recoverable
|
||||
function evaluate(code: string): boolean {
|
||||
// each evalContext is a separate function body, and we want strict mode to
|
||||
// work, so we should ensure that the code starts with "use strict"
|
||||
const [result, errInfo] = core.evalContext(`"use strict";\n\n${code}`);
|
||||
if (!errInfo) {
|
||||
// when a function is eval'ed with just "use strict" sometimes the result
|
||||
// is "use strict" which should be discarded
|
||||
lastEvalResult = typeof result === "string" && result === "use strict"
|
||||
? undefined
|
||||
: result;
|
||||
if (!isCloseCalled()) {
|
||||
replLog(lastEvalResult);
|
||||
}
|
||||
} else if (errInfo.isCompileError && isRecoverableError(errInfo.thrown)) {
|
||||
// Recoverable compiler error
|
||||
return false; // don't consume code.
|
||||
} else {
|
||||
lastThrownError = errInfo.thrown;
|
||||
if (errInfo.isNativeError) {
|
||||
const formattedError = core.formatError(errInfo.thrown as Error);
|
||||
replError(formattedError);
|
||||
} else {
|
||||
replError("Thrown:", errInfo.thrown);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// @internal
|
||||
export async function replLoop(): Promise<void> {
|
||||
const { console } = globalThis;
|
||||
|
||||
const historyFile = "deno_history.txt";
|
||||
const rid = startRepl(historyFile);
|
||||
|
||||
const quitRepl = (exitCode: number): void => {
|
||||
// Special handling in case user calls deno.close(3).
|
||||
try {
|
||||
close(rid); // close signals Drop on REPL and saves history.
|
||||
} catch {}
|
||||
exit(exitCode);
|
||||
};
|
||||
|
||||
// Configure globalThis._ to give the last evaluation result.
|
||||
Object.defineProperty(globalThis, "_", {
|
||||
configurable: true,
|
||||
get: (): Value => lastEvalResult,
|
||||
set: (value: Value): Value => {
|
||||
Object.defineProperty(globalThis, "_", {
|
||||
value: value,
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
console.log("Last evaluation result is no longer saved to _.");
|
||||
},
|
||||
});
|
||||
|
||||
// Configure globalThis._error to give the last thrown error.
|
||||
Object.defineProperty(globalThis, "_error", {
|
||||
configurable: true,
|
||||
get: (): Value => lastThrownError,
|
||||
set: (value: Value): Value => {
|
||||
Object.defineProperty(globalThis, "_error", {
|
||||
value: value,
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
console.log("Last thrown error is no longer saved to _error.");
|
||||
},
|
||||
});
|
||||
|
||||
replLog(`Deno ${version.deno}`);
|
||||
replLog("exit using ctrl+d or close()");
|
||||
|
||||
while (true) {
|
||||
if (isCloseCalled()) {
|
||||
quitRepl(0);
|
||||
}
|
||||
|
||||
let code = "";
|
||||
// Top level read
|
||||
try {
|
||||
code = await readline(rid, "> ");
|
||||
if (code.trim() === "") {
|
||||
continue;
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.message === "EOF") {
|
||||
quitRepl(0);
|
||||
} else {
|
||||
// If interrupted, don't print error.
|
||||
if (err.message !== "Interrupted") {
|
||||
// e.g. this happens when we have deno.close(3).
|
||||
// We want to display the problem.
|
||||
const formattedError = core.formatError(err);
|
||||
replError(formattedError);
|
||||
}
|
||||
// Quit REPL anyways.
|
||||
quitRepl(1);
|
||||
}
|
||||
}
|
||||
// Start continued read
|
||||
while (!evaluate(code)) {
|
||||
code += "\n";
|
||||
try {
|
||||
code += await readline(rid, " ");
|
||||
} catch (err) {
|
||||
// If interrupted on continued read,
|
||||
// abort this read instead of quitting.
|
||||
if (err.message === "Interrupted") {
|
||||
break;
|
||||
} else if (err.message === "EOF") {
|
||||
quitRepl(0);
|
||||
} else {
|
||||
// e.g. this happens when we have deno.close(3).
|
||||
// We want to display the problem.
|
||||
const formattedError = core.formatError(err);
|
||||
replError(formattedError);
|
||||
quitRepl(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { core } from "./core.ts";
|
||||
import * as dispatchMinimal from "./ops/dispatch_minimal.ts";
|
||||
import * as dispatchJson from "./ops/dispatch_json.ts";
|
||||
import * as util from "./util.ts";
|
||||
import { setBuildInfo } from "./build.ts";
|
||||
import { setVersions } from "./version.ts";
|
||||
import { setPrepareStackTrace } from "./error_stack.ts";
|
||||
import { Start, opStart } from "./ops/runtime.ts";
|
||||
import { handleTimerMacrotask } from "./web/timers.ts";
|
||||
|
||||
function getAsyncHandler(opName: string): (msg: Uint8Array) => void {
|
||||
switch (opName) {
|
||||
case "op_write":
|
||||
case "op_read":
|
||||
return dispatchMinimal.asyncMsgFromRust;
|
||||
default:
|
||||
return dispatchJson.asyncMsgFromRust;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(bartlomieju): temporary solution, must be fixed when moving
|
||||
// dispatches to separate crates
|
||||
export function initOps(): void {
|
||||
const opsMap = core.ops();
|
||||
for (const [name, opId] of Object.entries(opsMap)) {
|
||||
core.setAsyncHandler(opId, getAsyncHandler(name));
|
||||
}
|
||||
core.setMacrotaskCallback(handleTimerMacrotask);
|
||||
}
|
||||
|
||||
export function start(source?: string): Start {
|
||||
initOps();
|
||||
// First we send an empty `Start` message to let the privileged side know we
|
||||
// are ready. The response should be a `StartRes` message containing the CLI
|
||||
// args and other info.
|
||||
const s = opStart();
|
||||
setVersions(s.denoVersion, s.v8Version, s.tsVersion);
|
||||
setBuildInfo(s.target);
|
||||
util.setLogDebug(s.debugFlag, source);
|
||||
setPrepareStackTrace(Error);
|
||||
return s;
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// This module is the entry point for "main" isolate, ie. the one
|
||||
// that is created when you run "deno" executable.
|
||||
//
|
||||
// It provides a single function that should be called by Rust:
|
||||
// - `bootstrapMainRuntime` - must be called once, when Isolate is created.
|
||||
// It sets up runtime by providing globals for `WindowScope` and adds `Deno` global.
|
||||
|
||||
import * as denoNs from "./deno.ts";
|
||||
import * as denoUnstableNs from "./deno_unstable.ts";
|
||||
import { opMainModule } from "./ops/runtime.ts";
|
||||
import { exit } from "./ops/os.ts";
|
||||
import {
|
||||
readOnly,
|
||||
getterOnly,
|
||||
writable,
|
||||
windowOrWorkerGlobalScopeMethods,
|
||||
windowOrWorkerGlobalScopeProperties,
|
||||
eventTargetProperties,
|
||||
setEventTargetData,
|
||||
} from "./globals.ts";
|
||||
import { unstableMethods, unstableProperties } from "./globals_unstable.ts";
|
||||
import { internalObject, internalSymbol } from "./internals.ts";
|
||||
import { setSignals } from "./signals.ts";
|
||||
import { replLoop } from "./repl.ts";
|
||||
import { setTimeout } from "./web/timers.ts";
|
||||
import * as runtime from "./runtime.ts";
|
||||
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.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(denoNs as any)[internalSymbol] = internalObject;
|
||||
|
||||
let windowIsClosing = false;
|
||||
|
||||
function windowClose(): void {
|
||||
if (!windowIsClosing) {
|
||||
windowIsClosing = true;
|
||||
// Push a macrotask to exit after a promise resolve.
|
||||
// This is not perfect, but should be fine for first pass.
|
||||
Promise.resolve().then(() =>
|
||||
setTimeout.call(
|
||||
null,
|
||||
() => {
|
||||
// This should be fine, since only Window/MainWorker has .close()
|
||||
exit(0);
|
||||
},
|
||||
0,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const mainRuntimeGlobalProperties = {
|
||||
window: readOnly(globalThis),
|
||||
self: readOnly(globalThis),
|
||||
// TODO(bartlomieju): from MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope)
|
||||
// it seems those two properties should be available to workers as well
|
||||
onload: writable(null),
|
||||
onunload: writable(null),
|
||||
close: writable(windowClose),
|
||||
closed: getterOnly(() => windowIsClosing),
|
||||
};
|
||||
|
||||
let hasBootstrapped = false;
|
||||
|
||||
export function bootstrapMainRuntime(): void {
|
||||
if (hasBootstrapped) {
|
||||
throw new Error("Worker runtime already bootstrapped");
|
||||
}
|
||||
// Remove bootstrapping methods from global scope
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(globalThis as any).bootstrap = undefined;
|
||||
log("bootstrapMainRuntime");
|
||||
hasBootstrapped = true;
|
||||
Object.defineProperties(globalThis, windowOrWorkerGlobalScopeMethods);
|
||||
Object.defineProperties(globalThis, windowOrWorkerGlobalScopeProperties);
|
||||
Object.defineProperties(globalThis, eventTargetProperties);
|
||||
Object.defineProperties(globalThis, mainRuntimeGlobalProperties);
|
||||
setEventTargetData(globalThis);
|
||||
// Registers the handler for window.onload function.
|
||||
globalThis.addEventListener("load", (e) => {
|
||||
const { onload } = globalThis;
|
||||
if (typeof onload === "function") {
|
||||
onload(e);
|
||||
}
|
||||
});
|
||||
// Registers the handler for window.onunload function.
|
||||
globalThis.addEventListener("unload", (e) => {
|
||||
const { onunload } = globalThis;
|
||||
if (typeof onunload === "function") {
|
||||
onunload(e);
|
||||
}
|
||||
});
|
||||
|
||||
const { args, cwd, noColor, pid, ppid, repl, unstableFlag } = runtime.start();
|
||||
|
||||
Object.defineProperties(denoNs, {
|
||||
pid: readOnly(pid),
|
||||
ppid: readOnly(ppid),
|
||||
noColor: readOnly(noColor),
|
||||
args: readOnly(Object.freeze(args)),
|
||||
});
|
||||
|
||||
if (unstableFlag) {
|
||||
Object.defineProperties(globalThis, unstableMethods);
|
||||
Object.defineProperties(globalThis, unstableProperties);
|
||||
Object.defineProperty(denoNs, "mainModule", getterOnly(opMainModule));
|
||||
Object.assign(denoNs, denoUnstableNs);
|
||||
}
|
||||
|
||||
// Setup `Deno` global - we're actually overriding already
|
||||
// existing global `Deno` with `Deno` namespace from "./deno.ts".
|
||||
immutableDefine(globalThis, "Deno", denoNs);
|
||||
Object.freeze(globalThis.Deno);
|
||||
Object.freeze(globalThis.Deno.core);
|
||||
Object.freeze(globalThis.Deno.core.sharedQueue);
|
||||
setSignals();
|
||||
|
||||
log("cwd", cwd);
|
||||
log("args", args);
|
||||
|
||||
if (repl) {
|
||||
replLoop();
|
||||
}
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// This module is the entry point for "worker" isolate, ie. the one
|
||||
// that is created using `new Worker()` JS API.
|
||||
//
|
||||
// It provides a single function that should be called by Rust:
|
||||
// - `bootstrapWorkerRuntime` - must be called once, when Isolate is created.
|
||||
// It sets up runtime by providing globals for `DedicatedWorkerScope`.
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import {
|
||||
readOnly,
|
||||
writable,
|
||||
nonEnumerable,
|
||||
windowOrWorkerGlobalScopeMethods,
|
||||
windowOrWorkerGlobalScopeProperties,
|
||||
eventTargetProperties,
|
||||
setEventTargetData,
|
||||
} from "./globals.ts";
|
||||
import { unstableMethods, unstableProperties } from "./globals_unstable.ts";
|
||||
import * as denoNs from "./deno.ts";
|
||||
import * as denoUnstableNs from "./deno_unstable.ts";
|
||||
import * as webWorkerOps from "./ops/web_worker.ts";
|
||||
import { log, assert, immutableDefine } from "./util.ts";
|
||||
import { ErrorEventImpl as ErrorEvent } from "./web/error_event.ts";
|
||||
import { MessageEvent } from "./web/workers.ts";
|
||||
import { TextEncoder } from "./web/text_encoding.ts";
|
||||
import * as runtime from "./runtime.ts";
|
||||
import { internalObject, internalSymbol } from "./internals.ts";
|
||||
import { setSignals } from "./signals.ts";
|
||||
|
||||
// FIXME(bartlomieju): duplicated in `runtime_main.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.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(denoNs as any)[internalSymbol] = internalObject;
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
// TODO(bartlomieju): remove these funtions
|
||||
// Stuff for workers
|
||||
export const onmessage: (e: { data: any }) => void = (): void => {};
|
||||
export const onerror: (e: { data: any }) => void = (): void => {};
|
||||
|
||||
export function postMessage(data: any): void {
|
||||
const dataJson = JSON.stringify(data);
|
||||
const dataIntArray = encoder.encode(dataJson);
|
||||
webWorkerOps.postMessage(dataIntArray);
|
||||
}
|
||||
|
||||
let isClosing = false;
|
||||
let hasBootstrapped = false;
|
||||
|
||||
export function close(): void {
|
||||
if (isClosing) {
|
||||
return;
|
||||
}
|
||||
|
||||
isClosing = true;
|
||||
webWorkerOps.close();
|
||||
}
|
||||
|
||||
export async function workerMessageRecvCallback(data: string): Promise<void> {
|
||||
const msgEvent = new MessageEvent("message", {
|
||||
cancelable: false,
|
||||
data,
|
||||
});
|
||||
|
||||
try {
|
||||
if (globalThis["onmessage"]) {
|
||||
const result = globalThis.onmessage!(msgEvent);
|
||||
if (result && "then" in result) {
|
||||
await result;
|
||||
}
|
||||
}
|
||||
globalThis.dispatchEvent(msgEvent);
|
||||
} catch (e) {
|
||||
let handled = false;
|
||||
|
||||
const errorEvent = new ErrorEvent("error", {
|
||||
cancelable: true,
|
||||
message: e.message,
|
||||
lineno: e.lineNumber ? e.lineNumber + 1 : undefined,
|
||||
colno: e.columnNumber ? e.columnNumber + 1 : undefined,
|
||||
filename: e.fileName,
|
||||
error: null,
|
||||
});
|
||||
|
||||
if (globalThis["onerror"]) {
|
||||
const ret = globalThis.onerror(
|
||||
e.message,
|
||||
e.fileName,
|
||||
e.lineNumber,
|
||||
e.columnNumber,
|
||||
e,
|
||||
);
|
||||
handled = ret === true;
|
||||
}
|
||||
|
||||
globalThis.dispatchEvent(errorEvent);
|
||||
if (errorEvent.defaultPrevented) {
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const workerRuntimeGlobalProperties = {
|
||||
self: readOnly(globalThis),
|
||||
onmessage: writable(onmessage),
|
||||
onerror: writable(onerror),
|
||||
// TODO: should be readonly?
|
||||
close: nonEnumerable(close),
|
||||
postMessage: writable(postMessage),
|
||||
workerMessageRecvCallback: nonEnumerable(workerMessageRecvCallback),
|
||||
};
|
||||
|
||||
export function bootstrapWorkerRuntime(
|
||||
name: string,
|
||||
useDenoNamespace: boolean,
|
||||
internalName?: string,
|
||||
): void {
|
||||
if (hasBootstrapped) {
|
||||
throw new Error("Worker runtime already bootstrapped");
|
||||
}
|
||||
// Remove bootstrapping methods from global scope
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(globalThis as any).bootstrap = undefined;
|
||||
log("bootstrapWorkerRuntime");
|
||||
hasBootstrapped = true;
|
||||
Object.defineProperties(globalThis, windowOrWorkerGlobalScopeMethods);
|
||||
Object.defineProperties(globalThis, windowOrWorkerGlobalScopeProperties);
|
||||
Object.defineProperties(globalThis, workerRuntimeGlobalProperties);
|
||||
Object.defineProperties(globalThis, eventTargetProperties);
|
||||
Object.defineProperties(globalThis, { name: readOnly(name) });
|
||||
setEventTargetData(globalThis);
|
||||
const { unstableFlag, pid, noColor, args } = runtime.start(
|
||||
internalName ?? name,
|
||||
);
|
||||
|
||||
if (unstableFlag) {
|
||||
Object.defineProperties(globalThis, unstableMethods);
|
||||
Object.defineProperties(globalThis, unstableProperties);
|
||||
}
|
||||
|
||||
if (useDenoNamespace) {
|
||||
if (unstableFlag) {
|
||||
Object.assign(denoNs, denoUnstableNs);
|
||||
}
|
||||
Object.defineProperties(denoNs, {
|
||||
pid: readOnly(pid),
|
||||
noColor: readOnly(noColor),
|
||||
args: readOnly(Object.freeze(args)),
|
||||
});
|
||||
// Setup `Deno` global - we're actually overriding already
|
||||
// existing global `Deno` with `Deno` namespace from "./deno.ts".
|
||||
immutableDefine(globalThis, "Deno", denoNs);
|
||||
Object.freeze(globalThis.Deno);
|
||||
Object.freeze(globalThis.Deno.core);
|
||||
Object.freeze(globalThis.Deno.core.sharedQueue);
|
||||
setSignals();
|
||||
} else {
|
||||
delete globalThis.Deno;
|
||||
assert(globalThis.Deno === undefined);
|
||||
}
|
||||
}
|
|
@ -1,173 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { bindSignal, pollSignal, unbindSignal } from "./ops/signal.ts";
|
||||
import { build } from "./build.ts";
|
||||
|
||||
// From `kill -l`
|
||||
enum LinuxSignal {
|
||||
SIGHUP = 1,
|
||||
SIGINT = 2,
|
||||
SIGQUIT = 3,
|
||||
SIGILL = 4,
|
||||
SIGTRAP = 5,
|
||||
SIGABRT = 6,
|
||||
SIGBUS = 7,
|
||||
SIGFPE = 8,
|
||||
SIGKILL = 9,
|
||||
SIGUSR1 = 10,
|
||||
SIGSEGV = 11,
|
||||
SIGUSR2 = 12,
|
||||
SIGPIPE = 13,
|
||||
SIGALRM = 14,
|
||||
SIGTERM = 15,
|
||||
SIGSTKFLT = 16,
|
||||
SIGCHLD = 17,
|
||||
SIGCONT = 18,
|
||||
SIGSTOP = 19,
|
||||
SIGTSTP = 20,
|
||||
SIGTTIN = 21,
|
||||
SIGTTOU = 22,
|
||||
SIGURG = 23,
|
||||
SIGXCPU = 24,
|
||||
SIGXFSZ = 25,
|
||||
SIGVTALRM = 26,
|
||||
SIGPROF = 27,
|
||||
SIGWINCH = 28,
|
||||
SIGIO = 29,
|
||||
SIGPWR = 30,
|
||||
SIGSYS = 31,
|
||||
}
|
||||
|
||||
// From `kill -l`
|
||||
enum MacOSSignal {
|
||||
SIGHUP = 1,
|
||||
SIGINT = 2,
|
||||
SIGQUIT = 3,
|
||||
SIGILL = 4,
|
||||
SIGTRAP = 5,
|
||||
SIGABRT = 6,
|
||||
SIGEMT = 7,
|
||||
SIGFPE = 8,
|
||||
SIGKILL = 9,
|
||||
SIGBUS = 10,
|
||||
SIGSEGV = 11,
|
||||
SIGSYS = 12,
|
||||
SIGPIPE = 13,
|
||||
SIGALRM = 14,
|
||||
SIGTERM = 15,
|
||||
SIGURG = 16,
|
||||
SIGSTOP = 17,
|
||||
SIGTSTP = 18,
|
||||
SIGCONT = 19,
|
||||
SIGCHLD = 20,
|
||||
SIGTTIN = 21,
|
||||
SIGTTOU = 22,
|
||||
SIGIO = 23,
|
||||
SIGXCPU = 24,
|
||||
SIGXFSZ = 25,
|
||||
SIGVTALRM = 26,
|
||||
SIGPROF = 27,
|
||||
SIGWINCH = 28,
|
||||
SIGINFO = 29,
|
||||
SIGUSR1 = 30,
|
||||
SIGUSR2 = 31,
|
||||
}
|
||||
|
||||
export const Signal: { [key: string]: number } = {};
|
||||
|
||||
export function setSignals(): void {
|
||||
if (build.os === "darwin") {
|
||||
Object.assign(Signal, MacOSSignal);
|
||||
} else {
|
||||
Object.assign(Signal, LinuxSignal);
|
||||
}
|
||||
}
|
||||
|
||||
export function signal(signo: number): SignalStream {
|
||||
if (build.os === "windows") {
|
||||
throw new Error("not implemented!");
|
||||
}
|
||||
return new SignalStream(signo);
|
||||
}
|
||||
|
||||
export const signals = {
|
||||
alarm(): SignalStream {
|
||||
return signal(Signal.SIGALRM);
|
||||
},
|
||||
child(): SignalStream {
|
||||
return signal(Signal.SIGCHLD);
|
||||
},
|
||||
hungup(): SignalStream {
|
||||
return signal(Signal.SIGHUP);
|
||||
},
|
||||
interrupt(): SignalStream {
|
||||
return signal(Signal.SIGINT);
|
||||
},
|
||||
io(): SignalStream {
|
||||
return signal(Signal.SIGIO);
|
||||
},
|
||||
pipe(): SignalStream {
|
||||
return signal(Signal.SIGPIPE);
|
||||
},
|
||||
quit(): SignalStream {
|
||||
return signal(Signal.SIGQUIT);
|
||||
},
|
||||
terminate(): SignalStream {
|
||||
return signal(Signal.SIGTERM);
|
||||
},
|
||||
userDefined1(): SignalStream {
|
||||
return signal(Signal.SIGUSR1);
|
||||
},
|
||||
userDefined2(): SignalStream {
|
||||
return signal(Signal.SIGUSR2);
|
||||
},
|
||||
windowChange(): SignalStream {
|
||||
return signal(Signal.SIGWINCH);
|
||||
},
|
||||
};
|
||||
|
||||
export class SignalStream
|
||||
implements AsyncIterableIterator<void>, PromiseLike<void> {
|
||||
#disposed = false;
|
||||
#pollingPromise: Promise<boolean> = Promise.resolve(false);
|
||||
readonly #rid: number;
|
||||
|
||||
constructor(signo: number) {
|
||||
this.#rid = bindSignal(signo).rid;
|
||||
this.#loop();
|
||||
}
|
||||
|
||||
#pollSignal = async (): Promise<boolean> => {
|
||||
const res = await pollSignal(this.#rid);
|
||||
return res.done;
|
||||
};
|
||||
|
||||
#loop = async (): Promise<void> => {
|
||||
do {
|
||||
this.#pollingPromise = this.#pollSignal();
|
||||
} while (!(await this.#pollingPromise) && !this.#disposed);
|
||||
};
|
||||
|
||||
then<T, S>(
|
||||
f: (v: void) => T | Promise<T>,
|
||||
g?: (v: Error) => S | Promise<S>,
|
||||
): Promise<T | S> {
|
||||
return this.#pollingPromise.then(() => {}).then(f, g);
|
||||
}
|
||||
|
||||
async next(): Promise<IteratorResult<void>> {
|
||||
return { done: await this.#pollingPromise, value: undefined };
|
||||
}
|
||||
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<void> {
|
||||
return this;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
if (this.#disposed) {
|
||||
throw new Error("The stream has already been disposed.");
|
||||
}
|
||||
this.#disposed = true;
|
||||
unbindSignal(this.#rid);
|
||||
}
|
||||
}
|
|
@ -1,387 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { gray, green, italic, red, yellow } from "./colors.ts";
|
||||
import { exit } from "./ops/os.ts";
|
||||
import { Console, inspectArgs } from "./web/console.ts";
|
||||
import { stdout } from "./files.ts";
|
||||
import { exposeForTest } from "./internals.ts";
|
||||
import { TextEncoder } from "./web/text_encoding.ts";
|
||||
import { metrics } from "./ops/runtime.ts";
|
||||
import { resources } from "./ops/resources.ts";
|
||||
import { assert } from "./util.ts";
|
||||
|
||||
const disabledConsole = new Console((): void => {});
|
||||
|
||||
function delay(ms: number): Promise<void> {
|
||||
return new Promise((resolve: () => void) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
function formatDuration(time = 0): string {
|
||||
const timeStr = `(${time}ms)`;
|
||||
return gray(italic(timeStr));
|
||||
}
|
||||
|
||||
// Wrap test function in additional assertion that makes sure
|
||||
// the test case does not leak async "ops" - ie. number of async
|
||||
// completed ops after the test is the same as number of dispatched
|
||||
// ops. Note that "unref" ops are ignored since in nature that are
|
||||
// optional.
|
||||
function assertOps(fn: () => void | Promise<void>): () => void | Promise<void> {
|
||||
return async function asyncOpSanitizer(): Promise<void> {
|
||||
const pre = metrics();
|
||||
await fn();
|
||||
// Defer until next event loop turn - that way timeouts and intervals
|
||||
// cleared can actually be removed from resource table, otherwise
|
||||
// false positives may occur (https://github.com/denoland/deno/issues/4591)
|
||||
await delay(0);
|
||||
const post = metrics();
|
||||
// We're checking diff because one might spawn HTTP server in the background
|
||||
// that will be a pending async op before test starts.
|
||||
const dispatchedDiff = post.opsDispatchedAsync - pre.opsDispatchedAsync;
|
||||
const completedDiff = post.opsCompletedAsync - pre.opsCompletedAsync;
|
||||
assert(
|
||||
dispatchedDiff === completedDiff,
|
||||
`Test case is leaking async ops.
|
||||
Before:
|
||||
- dispatched: ${pre.opsDispatchedAsync}
|
||||
- completed: ${pre.opsCompletedAsync}
|
||||
After:
|
||||
- dispatched: ${post.opsDispatchedAsync}
|
||||
- completed: ${post.opsCompletedAsync}
|
||||
|
||||
Make sure to await all promises returned from Deno APIs before
|
||||
finishing test case.`,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
// Wrap test function in additional assertion that makes sure
|
||||
// the test case does not "leak" resources - ie. resource table after
|
||||
// the test has exactly the same contents as before the test.
|
||||
function assertResources(
|
||||
fn: () => void | Promise<void>,
|
||||
): () => void | Promise<void> {
|
||||
return async function resourceSanitizer(): Promise<void> {
|
||||
const pre = resources();
|
||||
await fn();
|
||||
const post = resources();
|
||||
|
||||
const preStr = JSON.stringify(pre, null, 2);
|
||||
const postStr = JSON.stringify(post, null, 2);
|
||||
const msg = `Test case is leaking resources.
|
||||
Before: ${preStr}
|
||||
After: ${postStr}
|
||||
|
||||
Make sure to close all open resource handles returned from Deno APIs before
|
||||
finishing test case.`;
|
||||
assert(preStr === postStr, msg);
|
||||
};
|
||||
}
|
||||
|
||||
export interface TestDefinition {
|
||||
fn: () => void | Promise<void>;
|
||||
name: string;
|
||||
ignore?: boolean;
|
||||
only?: boolean;
|
||||
sanitizeOps?: boolean;
|
||||
sanitizeResources?: boolean;
|
||||
}
|
||||
|
||||
const TEST_REGISTRY: TestDefinition[] = [];
|
||||
|
||||
export function test(t: TestDefinition): void;
|
||||
export function test(name: string, fn: () => void | Promise<void>): void;
|
||||
// Main test function provided by Deno, as you can see it merely
|
||||
// creates a new object with "name" and "fn" fields.
|
||||
export function test(
|
||||
t: string | TestDefinition,
|
||||
fn?: () => void | Promise<void>,
|
||||
): void {
|
||||
let testDef: TestDefinition;
|
||||
const defaults = {
|
||||
ignore: false,
|
||||
only: false,
|
||||
sanitizeOps: true,
|
||||
sanitizeResources: true,
|
||||
};
|
||||
|
||||
if (typeof t === "string") {
|
||||
if (!fn || typeof fn != "function") {
|
||||
throw new TypeError("Missing test function");
|
||||
}
|
||||
if (!t) {
|
||||
throw new TypeError("The test name can't be empty");
|
||||
}
|
||||
testDef = { fn: fn as () => void | Promise<void>, name: t, ...defaults };
|
||||
} else {
|
||||
if (!t.fn) {
|
||||
throw new TypeError("Missing test function");
|
||||
}
|
||||
if (!t.name) {
|
||||
throw new TypeError("The test name can't be empty");
|
||||
}
|
||||
testDef = { ...defaults, ...t };
|
||||
}
|
||||
|
||||
if (testDef.sanitizeOps) {
|
||||
testDef.fn = assertOps(testDef.fn);
|
||||
}
|
||||
|
||||
if (testDef.sanitizeResources) {
|
||||
testDef.fn = assertResources(testDef.fn);
|
||||
}
|
||||
|
||||
TEST_REGISTRY.push(testDef);
|
||||
}
|
||||
|
||||
interface TestMessage {
|
||||
start?: {
|
||||
tests: TestDefinition[];
|
||||
};
|
||||
// Must be extensible, avoiding `testStart?: TestDefinition;`.
|
||||
testStart?: {
|
||||
[P in keyof TestDefinition]: TestDefinition[P];
|
||||
};
|
||||
testEnd?: {
|
||||
name: string;
|
||||
status: "passed" | "failed" | "ignored";
|
||||
duration: number;
|
||||
error?: Error;
|
||||
};
|
||||
end?: {
|
||||
filtered: number;
|
||||
ignored: number;
|
||||
measured: number;
|
||||
passed: number;
|
||||
failed: number;
|
||||
usedOnly: boolean;
|
||||
duration: number;
|
||||
results: Array<TestMessage["testEnd"] & {}>;
|
||||
};
|
||||
}
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
function log(msg: string, noNewLine = false): void {
|
||||
if (!noNewLine) {
|
||||
msg += "\n";
|
||||
}
|
||||
|
||||
// Using `stdout` here because it doesn't force new lines
|
||||
// compared to `console.log`; `core.print` on the other hand
|
||||
// is line-buffered and doesn't output message without newline
|
||||
stdout.writeSync(encoder.encode(msg));
|
||||
}
|
||||
|
||||
function reportToConsole(message: TestMessage): void {
|
||||
const redFailed = red("FAILED");
|
||||
const greenOk = green("ok");
|
||||
const yellowIgnored = yellow("ignored");
|
||||
if (message.start != null) {
|
||||
log(`running ${message.start.tests.length} tests`);
|
||||
} else if (message.testStart != null) {
|
||||
const { name } = message.testStart;
|
||||
|
||||
log(`test ${name} ... `, true);
|
||||
return;
|
||||
} else if (message.testEnd != null) {
|
||||
switch (message.testEnd.status) {
|
||||
case "passed":
|
||||
log(`${greenOk} ${formatDuration(message.testEnd.duration)}`);
|
||||
break;
|
||||
case "failed":
|
||||
log(`${redFailed} ${formatDuration(message.testEnd.duration)}`);
|
||||
break;
|
||||
case "ignored":
|
||||
log(`${yellowIgnored} ${formatDuration(message.testEnd.duration)}`);
|
||||
break;
|
||||
}
|
||||
} else if (message.end != null) {
|
||||
const failures = message.end.results.filter((m) => m.error != null);
|
||||
if (failures.length > 0) {
|
||||
log(`\nfailures:\n`);
|
||||
|
||||
for (const { name, error } of failures) {
|
||||
log(name);
|
||||
log(inspectArgs([error!]));
|
||||
log("");
|
||||
}
|
||||
|
||||
log(`failures:\n`);
|
||||
|
||||
for (const { name } of failures) {
|
||||
log(`\t${name}`);
|
||||
}
|
||||
}
|
||||
log(
|
||||
`\ntest result: ${message.end.failed ? redFailed : greenOk}. ` +
|
||||
`${message.end.passed} passed; ${message.end.failed} failed; ` +
|
||||
`${message.end.ignored} ignored; ${message.end.measured} measured; ` +
|
||||
`${message.end.filtered} filtered out ` +
|
||||
`${formatDuration(message.end.duration)}\n`,
|
||||
);
|
||||
|
||||
if (message.end.usedOnly && message.end.failed == 0) {
|
||||
log(`${redFailed} because the "only" option was used\n`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exposeForTest("reportToConsole", reportToConsole);
|
||||
|
||||
// TODO: already implements AsyncGenerator<RunTestsMessage>, but add as "implements to class"
|
||||
// TODO: implements PromiseLike<RunTestsEndResult>
|
||||
class TestRunner {
|
||||
readonly testsToRun: TestDefinition[];
|
||||
readonly stats = {
|
||||
filtered: 0,
|
||||
ignored: 0,
|
||||
measured: 0,
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
};
|
||||
readonly #usedOnly: boolean;
|
||||
|
||||
constructor(
|
||||
tests: TestDefinition[],
|
||||
public filterFn: (def: TestDefinition) => boolean,
|
||||
public failFast: boolean,
|
||||
) {
|
||||
const onlyTests = tests.filter(({ only }) => only);
|
||||
this.#usedOnly = onlyTests.length > 0;
|
||||
const unfilteredTests = this.#usedOnly ? onlyTests : tests;
|
||||
this.testsToRun = unfilteredTests.filter(filterFn);
|
||||
this.stats.filtered = unfilteredTests.length - this.testsToRun.length;
|
||||
}
|
||||
|
||||
async *[Symbol.asyncIterator](): AsyncIterator<TestMessage> {
|
||||
yield { start: { tests: this.testsToRun } };
|
||||
|
||||
const results: Array<TestMessage["testEnd"] & {}> = [];
|
||||
const suiteStart = +new Date();
|
||||
for (const test of this.testsToRun) {
|
||||
const endMessage: Partial<TestMessage["testEnd"] & {}> = {
|
||||
name: test.name,
|
||||
duration: 0,
|
||||
};
|
||||
yield { testStart: { ...test } };
|
||||
if (test.ignore) {
|
||||
endMessage.status = "ignored";
|
||||
this.stats.ignored++;
|
||||
} else {
|
||||
const start = +new Date();
|
||||
try {
|
||||
await test.fn();
|
||||
endMessage.status = "passed";
|
||||
this.stats.passed++;
|
||||
} catch (err) {
|
||||
endMessage.status = "failed";
|
||||
endMessage.error = err;
|
||||
this.stats.failed++;
|
||||
}
|
||||
endMessage.duration = +new Date() - start;
|
||||
}
|
||||
results.push(endMessage as TestMessage["testEnd"] & {});
|
||||
yield { testEnd: endMessage as TestMessage["testEnd"] };
|
||||
if (this.failFast && endMessage.error != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const duration = +new Date() - suiteStart;
|
||||
|
||||
yield {
|
||||
end: { ...this.stats, usedOnly: this.#usedOnly, duration, results },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function createFilterFn(
|
||||
filter: undefined | string | RegExp,
|
||||
skip: undefined | string | RegExp,
|
||||
): (def: TestDefinition) => boolean {
|
||||
return (def: TestDefinition): boolean => {
|
||||
let passes = true;
|
||||
|
||||
if (filter) {
|
||||
if (filter instanceof RegExp) {
|
||||
passes = passes && filter.test(def.name);
|
||||
} else if (filter.startsWith("/") && filter.endsWith("/")) {
|
||||
const filterAsRegex = new RegExp(filter.slice(1, filter.length - 1));
|
||||
passes = passes && filterAsRegex.test(def.name);
|
||||
} else {
|
||||
passes = passes && def.name.includes(filter);
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
if (skip instanceof RegExp) {
|
||||
passes = passes && !skip.test(def.name);
|
||||
} else {
|
||||
passes = passes && !def.name.includes(skip);
|
||||
}
|
||||
}
|
||||
|
||||
return passes;
|
||||
};
|
||||
}
|
||||
|
||||
exposeForTest("createFilterFn", createFilterFn);
|
||||
|
||||
interface RunTestsOptions {
|
||||
exitOnFail?: boolean;
|
||||
failFast?: boolean;
|
||||
filter?: string | RegExp;
|
||||
skip?: string | RegExp;
|
||||
disableLog?: boolean;
|
||||
reportToConsole?: boolean;
|
||||
onMessage?: (message: TestMessage) => void | Promise<void>;
|
||||
}
|
||||
|
||||
async function runTests({
|
||||
exitOnFail = true,
|
||||
failFast = false,
|
||||
filter = undefined,
|
||||
skip = undefined,
|
||||
disableLog = false,
|
||||
reportToConsole: reportToConsole_ = true,
|
||||
onMessage = undefined,
|
||||
}: RunTestsOptions = {}): Promise<TestMessage["end"] & {}> {
|
||||
const filterFn = createFilterFn(filter, skip);
|
||||
const testRunner = new TestRunner(TEST_REGISTRY, filterFn, failFast);
|
||||
|
||||
const originalConsole = globalThis.console;
|
||||
|
||||
if (disableLog) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(globalThis as any).console = disabledConsole;
|
||||
}
|
||||
|
||||
let endMsg: TestMessage["end"];
|
||||
|
||||
for await (const message of testRunner) {
|
||||
if (onMessage != null) {
|
||||
await onMessage(message);
|
||||
}
|
||||
if (reportToConsole_) {
|
||||
reportToConsole(message);
|
||||
}
|
||||
if (message.end != null) {
|
||||
endMsg = message.end;
|
||||
}
|
||||
}
|
||||
|
||||
if (disableLog) {
|
||||
globalThis.console = originalConsole;
|
||||
}
|
||||
|
||||
if ((endMsg!.failed > 0 || endMsg?.usedOnly) && exitOnFail) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return endMsg!;
|
||||
}
|
||||
|
||||
exposeForTest("runTests", runTests);
|
|
@ -1,77 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import * as tlsOps from "./ops/tls.ts";
|
||||
import { Listener, Conn, ConnImpl, ListenerImpl } from "./net.ts";
|
||||
|
||||
// TODO(ry) There are many configuration options to add...
|
||||
// https://docs.rs/rustls/0.16.0/rustls/struct.ClientConfig.html
|
||||
interface ConnectTlsOptions {
|
||||
transport?: "tcp";
|
||||
port: number;
|
||||
hostname?: string;
|
||||
certFile?: string;
|
||||
}
|
||||
|
||||
export async function connectTls({
|
||||
port,
|
||||
hostname = "127.0.0.1",
|
||||
transport = "tcp",
|
||||
certFile = undefined,
|
||||
}: ConnectTlsOptions): Promise<Conn> {
|
||||
const res = await tlsOps.connectTls({
|
||||
port,
|
||||
hostname,
|
||||
transport,
|
||||
certFile,
|
||||
});
|
||||
return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!);
|
||||
}
|
||||
|
||||
class TLSListenerImpl extends ListenerImpl {
|
||||
async accept(): Promise<Conn> {
|
||||
const res = await tlsOps.acceptTLS(this.rid);
|
||||
return new ConnImpl(res.rid, res.remoteAddr, res.localAddr);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ListenTlsOptions {
|
||||
port: number;
|
||||
hostname?: string;
|
||||
transport?: "tcp";
|
||||
certFile: string;
|
||||
keyFile: string;
|
||||
}
|
||||
|
||||
export function listenTls({
|
||||
port,
|
||||
certFile,
|
||||
keyFile,
|
||||
hostname = "0.0.0.0",
|
||||
transport = "tcp",
|
||||
}: ListenTlsOptions): Listener {
|
||||
const res = tlsOps.listenTls({
|
||||
port,
|
||||
certFile,
|
||||
keyFile,
|
||||
hostname,
|
||||
transport,
|
||||
});
|
||||
return new TLSListenerImpl(res.rid, res.localAddr);
|
||||
}
|
||||
|
||||
interface StartTlsOptions {
|
||||
hostname?: string;
|
||||
certFile?: string;
|
||||
}
|
||||
|
||||
export async function startTls(
|
||||
conn: Conn,
|
||||
{ hostname = "127.0.0.1", certFile }: StartTlsOptions = {},
|
||||
): Promise<Conn> {
|
||||
const res = await tlsOps.startTls({
|
||||
rid: conn.rid,
|
||||
hostname,
|
||||
certFile,
|
||||
});
|
||||
return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!);
|
||||
}
|
37
cli/js/ts_global.d.ts
vendored
37
cli/js/ts_global.d.ts
vendored
|
@ -1,37 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// This scopes the `ts` namespace globally, which is where it exists at runtime
|
||||
// when building Deno, but the `typescript/lib/typescript.d.ts` is defined as a
|
||||
// module.
|
||||
|
||||
// Warning! This is a magical import. We don't want to have multiple copies of
|
||||
// typescript.d.ts around the repo, there's already one in
|
||||
// deno_typescript/typescript/lib/typescript.d.ts. Ideally we could simply point
|
||||
// to that in this import specifier, but "cargo package" is very strict and
|
||||
// requires all files to be present in a crate's subtree.
|
||||
// to get proper editor intellisense, you can substitute "$asset$" with
|
||||
// "../../deno_typescript/typescript/lib" - remember to revert before committing
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import * as ts_ from "$asset$/typescript.d.ts";
|
||||
|
||||
declare global {
|
||||
namespace ts {
|
||||
export = ts_;
|
||||
}
|
||||
|
||||
namespace ts {
|
||||
// this are marked @internal in TypeScript, but we need to access them,
|
||||
// there is a risk these could change in future versions of TypeScript
|
||||
export const libs: string[];
|
||||
export const libMap: Map<string, string>;
|
||||
export const performance: {
|
||||
enable(): void;
|
||||
disable(): void;
|
||||
getDuration(value: string): number;
|
||||
};
|
||||
|
||||
interface SourceFile {
|
||||
version?: string;
|
||||
}
|
||||
}
|
||||
}
|
126
cli/js/util.ts
126
cli/js/util.ts
|
@ -1,126 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { build } from "./build.ts";
|
||||
import { exposeForTest } from "./internals.ts";
|
||||
|
||||
let logDebug = false;
|
||||
let logSource = "JS";
|
||||
|
||||
// @internal
|
||||
export function setLogDebug(debug: boolean, source?: string): void {
|
||||
logDebug = debug;
|
||||
if (source) {
|
||||
logSource = source;
|
||||
}
|
||||
}
|
||||
|
||||
export function log(...args: unknown[]): void {
|
||||
if (logDebug) {
|
||||
// if we destructure `console` off `globalThis` too early, we don't bind to
|
||||
// the right console, therefore we don't log anything out.
|
||||
globalThis.console.log(`DEBUG ${logSource} -`, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
// @internal
|
||||
export class AssertionError extends Error {
|
||||
constructor(msg?: string) {
|
||||
super(msg);
|
||||
this.name = "AssertionError";
|
||||
}
|
||||
}
|
||||
|
||||
// @internal
|
||||
export function assert(cond: unknown, msg = "Assertion failed."): asserts cond {
|
||||
if (!cond) {
|
||||
throw new AssertionError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
export type ResolveFunction<T> = (value?: T | PromiseLike<T>) => void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type RejectFunction = (reason?: any) => void;
|
||||
|
||||
export interface ResolvableMethods<T> {
|
||||
resolve: ResolveFunction<T>;
|
||||
reject: RejectFunction;
|
||||
}
|
||||
|
||||
// @internal
|
||||
export type Resolvable<T> = Promise<T> & ResolvableMethods<T>;
|
||||
|
||||
// @internal
|
||||
export function createResolvable<T>(): Resolvable<T> {
|
||||
let resolve: ResolveFunction<T>;
|
||||
let reject: RejectFunction;
|
||||
const promise = new Promise<T>((res, rej): void => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
}) as Resolvable<T>;
|
||||
promise.resolve = resolve!;
|
||||
promise.reject = reject!;
|
||||
return promise;
|
||||
}
|
||||
|
||||
// @internal
|
||||
export function notImplemented(): never {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
// @internal
|
||||
export function immutableDefine(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
o: any,
|
||||
p: string | number | symbol,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any,
|
||||
): void {
|
||||
Object.defineProperty(o, p, {
|
||||
value,
|
||||
configurable: false,
|
||||
writable: false,
|
||||
});
|
||||
}
|
||||
|
||||
function pathFromURLWin32(url: URL): string {
|
||||
const hostname = url.hostname;
|
||||
const pathname = decodeURIComponent(url.pathname.replace(/\//g, "\\"));
|
||||
|
||||
if (hostname !== "") {
|
||||
//TODO(actual-size) Node adds a punycode decoding step, we should consider adding this
|
||||
return `\\\\${hostname}${pathname}`;
|
||||
}
|
||||
|
||||
const validPath = /^\\(?<driveLetter>[A-Za-z]):\\/;
|
||||
const matches = validPath.exec(pathname);
|
||||
|
||||
if (!matches?.groups?.driveLetter) {
|
||||
throw new TypeError("A URL with the file schema must be absolute.");
|
||||
}
|
||||
|
||||
// we don't want a leading slash on an absolute path in Windows
|
||||
return pathname.slice(1);
|
||||
}
|
||||
|
||||
function pathFromURLPosix(url: URL): string {
|
||||
if (url.hostname !== "") {
|
||||
throw new TypeError(`Host must be empty.`);
|
||||
}
|
||||
|
||||
return decodeURIComponent(url.pathname);
|
||||
}
|
||||
|
||||
export function pathFromURL(pathOrUrl: string | URL): string {
|
||||
if (pathOrUrl instanceof URL) {
|
||||
if (pathOrUrl.protocol != "file:") {
|
||||
throw new TypeError("Must be a file URL.");
|
||||
}
|
||||
|
||||
return build.os == "windows"
|
||||
? pathFromURLWin32(pathOrUrl)
|
||||
: pathFromURLPosix(pathOrUrl);
|
||||
}
|
||||
return pathOrUrl;
|
||||
}
|
||||
|
||||
exposeForTest("pathFromURL", pathFromURL);
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
interface Version {
|
||||
deno: string;
|
||||
v8: string;
|
||||
typescript: string;
|
||||
}
|
||||
|
||||
export const version: Version = {
|
||||
deno: "",
|
||||
v8: "",
|
||||
typescript: "",
|
||||
};
|
||||
|
||||
export function setVersions(
|
||||
denoVersion: string,
|
||||
v8Version: string,
|
||||
tsVersion: string,
|
||||
): void {
|
||||
version.deno = denoVersion;
|
||||
version.v8 = v8Version;
|
||||
version.typescript = tsVersion;
|
||||
|
||||
Object.freeze(version);
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
# Deno Web APIs
|
||||
|
||||
This directory facilities Web APIs that are available in Deno.
|
||||
|
||||
Please note, that some implementations might not be completely aligned with
|
||||
specification.
|
||||
|
||||
Some Web APIs are using ops under the hood, eg. `console`, `performance`.
|
||||
|
||||
## Implemented Web APIs
|
||||
|
||||
- [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob): for
|
||||
representing opaque binary data
|
||||
- [Console](https://developer.mozilla.org/en-US/docs/Web/API/Console): for
|
||||
logging purposes
|
||||
- [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent),
|
||||
[EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)
|
||||
and
|
||||
[EventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventListener):
|
||||
to work with DOM events
|
||||
- **Implementation notes:** There is no DOM hierarchy in Deno, so there is no
|
||||
tree for Events to bubble/capture through.
|
||||
- [fetch](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch),
|
||||
[Request](https://developer.mozilla.org/en-US/docs/Web/API/Request),
|
||||
[Response](https://developer.mozilla.org/en-US/docs/Web/API/Response),
|
||||
[Body](https://developer.mozilla.org/en-US/docs/Web/API/Body) and
|
||||
[Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers): modern
|
||||
Promise-based HTTP Request API
|
||||
- [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData): access
|
||||
to a `multipart/form-data` serialization
|
||||
- [Performance](https://developer.mozilla.org/en-US/docs/Web/API/Performance):
|
||||
retrieving current time with a high precision
|
||||
- [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout),
|
||||
[setInterval](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval),
|
||||
[clearTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearTimeout):
|
||||
scheduling callbacks in future and
|
||||
[clearInterval](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearInterval)
|
||||
- [Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) for
|
||||
creating, composing, and consuming streams of data
|
||||
- [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL) and
|
||||
[URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams):
|
||||
to construct and parse URLSs
|
||||
- [Worker](https://developer.mozilla.org/en-US/docs/Web/API/Worker): executing
|
||||
additional code in a separate thread
|
||||
- **Implementation notes:** Blob URLs are not supported, object ownership
|
||||
cannot be transferred, posted data is serialized to JSON instead of
|
||||
[structured cloning](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { AbortSignalImpl, signalAbort } from "./abort_signal.ts";
|
||||
|
||||
export class AbortControllerImpl implements AbortController {
|
||||
#signal = new AbortSignalImpl();
|
||||
|
||||
get signal(): AbortSignal {
|
||||
return this.#signal;
|
||||
}
|
||||
|
||||
abort(): void {
|
||||
this.#signal[signalAbort]();
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag](): string {
|
||||
return "AbortController";
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(AbortControllerImpl, "name", {
|
||||
value: "AbortController",
|
||||
configurable: true,
|
||||
});
|
|
@ -1,58 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
import { EventImpl } from "./event.ts";
|
||||
import { EventTargetImpl } from "./event_target.ts";
|
||||
|
||||
export const add = Symbol("add");
|
||||
export const signalAbort = Symbol("signalAbort");
|
||||
export const remove = Symbol("remove");
|
||||
|
||||
export class AbortSignalImpl extends EventTargetImpl implements AbortSignal {
|
||||
#aborted?: boolean;
|
||||
#abortAlgorithms = new Set<() => void>();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onabort: ((this: AbortSignal, ev: Event) => any) | null = null;
|
||||
|
||||
[add](algorithm: () => void): void {
|
||||
this.#abortAlgorithms.add(algorithm);
|
||||
}
|
||||
|
||||
[signalAbort](): void {
|
||||
if (this.#aborted) {
|
||||
return;
|
||||
}
|
||||
this.#aborted = true;
|
||||
for (const algorithm of this.#abortAlgorithms) {
|
||||
algorithm();
|
||||
}
|
||||
this.#abortAlgorithms.clear();
|
||||
this.dispatchEvent(new EventImpl("abort"));
|
||||
}
|
||||
|
||||
[remove](algorithm: () => void): void {
|
||||
this.#abortAlgorithms.delete(algorithm);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.addEventListener("abort", (evt: Event) => {
|
||||
const { onabort } = this;
|
||||
if (typeof onabort === "function") {
|
||||
onabort.call(this, evt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get aborted(): boolean {
|
||||
return Boolean(this.#aborted);
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag](): string {
|
||||
return "AbortSignal";
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(AbortSignalImpl, "name", {
|
||||
value: "AbortSignal",
|
||||
configurable: true,
|
||||
});
|
|
@ -1,148 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// Forked from https://github.com/beatgammit/base64-js
|
||||
// Copyright (c) 2014 Jameson Little. MIT License.
|
||||
|
||||
const lookup: string[] = [];
|
||||
const revLookup: number[] = [];
|
||||
|
||||
const code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
for (let i = 0, len = code.length; i < len; ++i) {
|
||||
lookup[i] = code[i];
|
||||
revLookup[code.charCodeAt(i)] = i;
|
||||
}
|
||||
|
||||
// Support decoding URL-safe base64 strings, as Node.js does.
|
||||
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
|
||||
revLookup["-".charCodeAt(0)] = 62;
|
||||
revLookup["_".charCodeAt(0)] = 63;
|
||||
|
||||
function getLens(b64: string): [number, number] {
|
||||
const len = b64.length;
|
||||
|
||||
if (len % 4 > 0) {
|
||||
throw new Error("Invalid string. Length must be a multiple of 4");
|
||||
}
|
||||
|
||||
// Trim off extra bytes after placeholder bytes are found
|
||||
// See: https://github.com/beatgammit/base64-js/issues/42
|
||||
let validLen = b64.indexOf("=");
|
||||
if (validLen === -1) validLen = len;
|
||||
|
||||
const placeHoldersLen = validLen === len ? 0 : 4 - (validLen % 4);
|
||||
|
||||
return [validLen, placeHoldersLen];
|
||||
}
|
||||
|
||||
// base64 is 4/3 + up to two characters of the original data
|
||||
export function byteLength(b64: string): number {
|
||||
const lens = getLens(b64);
|
||||
const validLen = lens[0];
|
||||
const placeHoldersLen = lens[1];
|
||||
return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen;
|
||||
}
|
||||
|
||||
function _byteLength(
|
||||
b64: string,
|
||||
validLen: number,
|
||||
placeHoldersLen: number,
|
||||
): number {
|
||||
return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen;
|
||||
}
|
||||
|
||||
export function toByteArray(b64: string): Uint8Array {
|
||||
let tmp;
|
||||
const lens = getLens(b64);
|
||||
const validLen = lens[0];
|
||||
const placeHoldersLen = lens[1];
|
||||
|
||||
const arr = new Uint8Array(_byteLength(b64, validLen, placeHoldersLen));
|
||||
|
||||
let curByte = 0;
|
||||
|
||||
// if there are placeholders, only get up to the last complete 4 chars
|
||||
const len = placeHoldersLen > 0 ? validLen - 4 : validLen;
|
||||
|
||||
let i;
|
||||
for (i = 0; i < len; i += 4) {
|
||||
tmp = (revLookup[b64.charCodeAt(i)] << 18) |
|
||||
(revLookup[b64.charCodeAt(i + 1)] << 12) |
|
||||
(revLookup[b64.charCodeAt(i + 2)] << 6) |
|
||||
revLookup[b64.charCodeAt(i + 3)];
|
||||
arr[curByte++] = (tmp >> 16) & 0xff;
|
||||
arr[curByte++] = (tmp >> 8) & 0xff;
|
||||
arr[curByte++] = tmp & 0xff;
|
||||
}
|
||||
|
||||
if (placeHoldersLen === 2) {
|
||||
tmp = (revLookup[b64.charCodeAt(i)] << 2) |
|
||||
(revLookup[b64.charCodeAt(i + 1)] >> 4);
|
||||
arr[curByte++] = tmp & 0xff;
|
||||
}
|
||||
|
||||
if (placeHoldersLen === 1) {
|
||||
tmp = (revLookup[b64.charCodeAt(i)] << 10) |
|
||||
(revLookup[b64.charCodeAt(i + 1)] << 4) |
|
||||
(revLookup[b64.charCodeAt(i + 2)] >> 2);
|
||||
arr[curByte++] = (tmp >> 8) & 0xff;
|
||||
arr[curByte++] = tmp & 0xff;
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
function tripletToBase64(num: number): string {
|
||||
return (
|
||||
lookup[(num >> 18) & 0x3f] +
|
||||
lookup[(num >> 12) & 0x3f] +
|
||||
lookup[(num >> 6) & 0x3f] +
|
||||
lookup[num & 0x3f]
|
||||
);
|
||||
}
|
||||
|
||||
function encodeChunk(uint8: Uint8Array, start: number, end: number): string {
|
||||
let tmp;
|
||||
const output = [];
|
||||
for (let i = start; i < end; i += 3) {
|
||||
tmp = ((uint8[i] << 16) & 0xff0000) +
|
||||
((uint8[i + 1] << 8) & 0xff00) +
|
||||
(uint8[i + 2] & 0xff);
|
||||
output.push(tripletToBase64(tmp));
|
||||
}
|
||||
return output.join("");
|
||||
}
|
||||
|
||||
export function fromByteArray(uint8: Uint8Array): string {
|
||||
let tmp;
|
||||
const len = uint8.length;
|
||||
const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes
|
||||
const parts = [];
|
||||
const maxChunkLength = 16383; // must be multiple of 3
|
||||
|
||||
// go through the array every three bytes, we'll deal with trailing stuff later
|
||||
for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
||||
parts.push(
|
||||
encodeChunk(
|
||||
uint8,
|
||||
i,
|
||||
i + maxChunkLength > len2 ? len2 : i + maxChunkLength,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// pad the end with zeros, but make sure to not forget the extra bytes
|
||||
if (extraBytes === 1) {
|
||||
tmp = uint8[len - 1];
|
||||
parts.push(lookup[tmp >> 2] + lookup[(tmp << 4) & 0x3f] + "==");
|
||||
} else if (extraBytes === 2) {
|
||||
tmp = (uint8[len - 2] << 8) + uint8[len - 1];
|
||||
parts.push(
|
||||
lookup[tmp >> 10] +
|
||||
lookup[(tmp >> 4) & 0x3f] +
|
||||
lookup[(tmp << 2) & 0x3f] +
|
||||
"=",
|
||||
);
|
||||
}
|
||||
|
||||
return parts.join("");
|
||||
}
|
|
@ -1,227 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { TextDecoder, TextEncoder } from "./text_encoding.ts";
|
||||
import { build } from "../build.ts";
|
||||
import { ReadableStreamImpl } from "./streams/readable_stream.ts";
|
||||
|
||||
export const bytesSymbol = Symbol("bytes");
|
||||
|
||||
export function containsOnlyASCII(str: string): boolean {
|
||||
if (typeof str !== "string") {
|
||||
return false;
|
||||
}
|
||||
return /^[\x00-\x7F]*$/.test(str);
|
||||
}
|
||||
|
||||
function convertLineEndingsToNative(s: string): string {
|
||||
const nativeLineEnd = build.os == "windows" ? "\r\n" : "\n";
|
||||
|
||||
let position = 0;
|
||||
|
||||
let collectionResult = collectSequenceNotCRLF(s, position);
|
||||
|
||||
let token = collectionResult.collected;
|
||||
position = collectionResult.newPosition;
|
||||
|
||||
let result = token;
|
||||
|
||||
while (position < s.length) {
|
||||
const c = s.charAt(position);
|
||||
if (c == "\r") {
|
||||
result += nativeLineEnd;
|
||||
position++;
|
||||
if (position < s.length && s.charAt(position) == "\n") {
|
||||
position++;
|
||||
}
|
||||
} else if (c == "\n") {
|
||||
position++;
|
||||
result += nativeLineEnd;
|
||||
}
|
||||
|
||||
collectionResult = collectSequenceNotCRLF(s, position);
|
||||
|
||||
token = collectionResult.collected;
|
||||
position = collectionResult.newPosition;
|
||||
|
||||
result += token;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function collectSequenceNotCRLF(
|
||||
s: string,
|
||||
position: number,
|
||||
): { collected: string; newPosition: number } {
|
||||
const start = position;
|
||||
for (
|
||||
let c = s.charAt(position);
|
||||
position < s.length && !(c == "\r" || c == "\n");
|
||||
c = s.charAt(++position)
|
||||
);
|
||||
return { collected: s.slice(start, position), newPosition: position };
|
||||
}
|
||||
|
||||
function toUint8Arrays(
|
||||
blobParts: BlobPart[],
|
||||
doNormalizeLineEndingsToNative: boolean,
|
||||
): Uint8Array[] {
|
||||
const ret: Uint8Array[] = [];
|
||||
const enc = new TextEncoder();
|
||||
for (const element of blobParts) {
|
||||
if (typeof element === "string") {
|
||||
let str = element;
|
||||
if (doNormalizeLineEndingsToNative) {
|
||||
str = convertLineEndingsToNative(element);
|
||||
}
|
||||
ret.push(enc.encode(str));
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
} else if (element instanceof DenoBlob) {
|
||||
ret.push(element[bytesSymbol]);
|
||||
} else if (element instanceof Uint8Array) {
|
||||
ret.push(element);
|
||||
} else if (element instanceof Uint16Array) {
|
||||
const uint8 = new Uint8Array(element.buffer);
|
||||
ret.push(uint8);
|
||||
} else if (element instanceof Uint32Array) {
|
||||
const uint8 = new Uint8Array(element.buffer);
|
||||
ret.push(uint8);
|
||||
} else if (ArrayBuffer.isView(element)) {
|
||||
// Convert view to Uint8Array.
|
||||
const uint8 = new Uint8Array(element.buffer);
|
||||
ret.push(uint8);
|
||||
} else if (element instanceof ArrayBuffer) {
|
||||
// Create a new Uint8Array view for the given ArrayBuffer.
|
||||
const uint8 = new Uint8Array(element);
|
||||
ret.push(uint8);
|
||||
} else {
|
||||
ret.push(enc.encode(String(element)));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function processBlobParts(
|
||||
blobParts: BlobPart[],
|
||||
options: BlobPropertyBag,
|
||||
): Uint8Array {
|
||||
const normalizeLineEndingsToNative = options.ending === "native";
|
||||
// ArrayBuffer.transfer is not yet implemented in V8, so we just have to
|
||||
// pre compute size of the array buffer and do some sort of static allocation
|
||||
// instead of dynamic allocation.
|
||||
const uint8Arrays = toUint8Arrays(blobParts, normalizeLineEndingsToNative);
|
||||
const byteLength = uint8Arrays
|
||||
.map((u8): number => u8.byteLength)
|
||||
.reduce((a, b): number => a + b, 0);
|
||||
const ab = new ArrayBuffer(byteLength);
|
||||
const bytes = new Uint8Array(ab);
|
||||
let courser = 0;
|
||||
for (const u8 of uint8Arrays) {
|
||||
bytes.set(u8, courser);
|
||||
courser += u8.byteLength;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function getStream(blobBytes: Uint8Array): ReadableStream<ArrayBufferView> {
|
||||
// TODO: Align to spec https://fetch.spec.whatwg.org/#concept-construct-readablestream
|
||||
return new ReadableStreamImpl({
|
||||
type: "bytes",
|
||||
start: (controller: ReadableByteStreamController): void => {
|
||||
controller.enqueue(blobBytes);
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function readBytes(
|
||||
reader: ReadableStreamReader<ArrayBufferView>,
|
||||
): Promise<ArrayBuffer> {
|
||||
const chunks: Uint8Array[] = [];
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (!done && value instanceof Uint8Array) {
|
||||
chunks.push(value);
|
||||
} else if (done) {
|
||||
const size = chunks.reduce((p, i) => p + i.byteLength, 0);
|
||||
const bytes = new Uint8Array(size);
|
||||
let offs = 0;
|
||||
for (const chunk of chunks) {
|
||||
bytes.set(chunk, offs);
|
||||
offs += chunk.byteLength;
|
||||
}
|
||||
return bytes;
|
||||
} else {
|
||||
throw new TypeError("Invalid reader result.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A WeakMap holding blob to byte array mapping.
|
||||
// Ensures it does not impact garbage collection.
|
||||
export const blobBytesWeakMap = new WeakMap<Blob, Uint8Array>();
|
||||
|
||||
class DenoBlob implements Blob {
|
||||
[bytesSymbol]: Uint8Array;
|
||||
readonly size: number = 0;
|
||||
readonly type: string = "";
|
||||
|
||||
constructor(blobParts?: BlobPart[], options?: BlobPropertyBag) {
|
||||
if (arguments.length === 0) {
|
||||
this[bytesSymbol] = new Uint8Array();
|
||||
return;
|
||||
}
|
||||
|
||||
const { ending = "transparent", type = "" } = options ?? {};
|
||||
// Normalize options.type.
|
||||
let normalizedType = type;
|
||||
if (!containsOnlyASCII(type)) {
|
||||
normalizedType = "";
|
||||
} else {
|
||||
if (type.length) {
|
||||
for (let i = 0; i < type.length; ++i) {
|
||||
const char = type[i];
|
||||
if (char < "\u0020" || char > "\u007E") {
|
||||
normalizedType = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
normalizedType = type.toLowerCase();
|
||||
}
|
||||
}
|
||||
const bytes = processBlobParts(blobParts!, { ending, type });
|
||||
// Set Blob object's properties.
|
||||
this[bytesSymbol] = bytes;
|
||||
this.size = bytes.byteLength;
|
||||
this.type = normalizedType;
|
||||
}
|
||||
|
||||
slice(start?: number, end?: number, contentType?: string): DenoBlob {
|
||||
return new DenoBlob([this[bytesSymbol].slice(start, end)], {
|
||||
type: contentType || this.type,
|
||||
});
|
||||
}
|
||||
|
||||
stream(): ReadableStream<ArrayBufferView> {
|
||||
return getStream(this[bytesSymbol]);
|
||||
}
|
||||
|
||||
async text(): Promise<string> {
|
||||
const reader = getStream(this[bytesSymbol]).getReader();
|
||||
const decoder = new TextDecoder();
|
||||
return decoder.decode(await readBytes(reader));
|
||||
}
|
||||
|
||||
arrayBuffer(): Promise<ArrayBuffer> {
|
||||
return readBytes(getStream(this[bytesSymbol]).getReader());
|
||||
}
|
||||
}
|
||||
|
||||
// we want the Base class name to be the name of the class.
|
||||
Object.defineProperty(DenoBlob, "name", {
|
||||
value: "Blob",
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
export { DenoBlob };
|
|
@ -1,213 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import * as blob from "./blob.ts";
|
||||
import * as encoding from "./text_encoding.ts";
|
||||
import type * as domTypes from "./dom_types.d.ts";
|
||||
import { ReadableStreamImpl } from "./streams/readable_stream.ts";
|
||||
import { isReadableStreamDisturbed } from "./streams/internals.ts";
|
||||
import { Buffer } from "../buffer.ts";
|
||||
|
||||
import {
|
||||
getHeaderValueParams,
|
||||
hasHeaderValueOf,
|
||||
isTypedArray,
|
||||
} from "./util.ts";
|
||||
import { MultipartParser } from "./fetch/multipart.ts";
|
||||
|
||||
// only namespace imports work for now, plucking out what we need
|
||||
const { TextEncoder, TextDecoder } = encoding;
|
||||
const DenoBlob = blob.DenoBlob;
|
||||
|
||||
interface BodyMeta {
|
||||
contentType: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
function validateBodyType(owner: Body, bodySource: BodyInit | null): boolean {
|
||||
if (isTypedArray(bodySource)) {
|
||||
return true;
|
||||
} else if (bodySource instanceof ArrayBuffer) {
|
||||
return true;
|
||||
} else if (typeof bodySource === "string") {
|
||||
return true;
|
||||
} else if (bodySource instanceof ReadableStreamImpl) {
|
||||
return true;
|
||||
} else if (bodySource instanceof FormData) {
|
||||
return true;
|
||||
} else if (bodySource instanceof URLSearchParams) {
|
||||
return true;
|
||||
} else if (!bodySource) {
|
||||
return true; // null body is fine
|
||||
}
|
||||
throw new Error(
|
||||
`Bad ${owner.constructor.name} body type: ${bodySource.constructor.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
async function bufferFromStream(
|
||||
stream: ReadableStreamReader,
|
||||
size?: number,
|
||||
): Promise<ArrayBuffer> {
|
||||
const encoder = new TextEncoder();
|
||||
const buffer = new Buffer();
|
||||
|
||||
if (size) {
|
||||
// grow to avoid unnecessary allocations & copies
|
||||
buffer.grow(size);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await stream.read();
|
||||
|
||||
if (done) break;
|
||||
|
||||
if (typeof value === "string") {
|
||||
buffer.writeSync(encoder.encode(value));
|
||||
} else if (value instanceof ArrayBuffer) {
|
||||
buffer.writeSync(new Uint8Array(value));
|
||||
} else if (value instanceof Uint8Array) {
|
||||
buffer.writeSync(value);
|
||||
} else if (!value) {
|
||||
// noop for undefined
|
||||
} else {
|
||||
throw new Error("unhandled type on stream read");
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.bytes().buffer;
|
||||
}
|
||||
|
||||
export const BodyUsedError =
|
||||
"Failed to execute 'clone' on 'Body': body is already used";
|
||||
|
||||
export class Body implements domTypes.Body {
|
||||
protected _stream: ReadableStreamImpl<string | ArrayBuffer> | null;
|
||||
#contentType: string;
|
||||
#size: number | undefined;
|
||||
constructor(protected _bodySource: BodyInit | null, meta: BodyMeta) {
|
||||
validateBodyType(this, _bodySource);
|
||||
this._bodySource = _bodySource;
|
||||
this.#contentType = meta.contentType;
|
||||
this.#size = meta.size;
|
||||
this._stream = null;
|
||||
}
|
||||
|
||||
get body(): ReadableStream | null {
|
||||
if (this._stream) {
|
||||
return this._stream;
|
||||
}
|
||||
|
||||
if (this._bodySource instanceof ReadableStreamImpl) {
|
||||
this._stream = this._bodySource;
|
||||
}
|
||||
if (typeof this._bodySource === "string") {
|
||||
const bodySource = this._bodySource;
|
||||
this._stream = new ReadableStreamImpl<string | ArrayBuffer>({
|
||||
start(controller: ReadableStreamDefaultController): void {
|
||||
controller.enqueue(bodySource);
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
}
|
||||
return this._stream;
|
||||
}
|
||||
|
||||
get bodyUsed(): boolean {
|
||||
if (this.body && isReadableStreamDisturbed(this.body)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async blob(): Promise<Blob> {
|
||||
return new DenoBlob([await this.arrayBuffer()], {
|
||||
type: this.#contentType,
|
||||
});
|
||||
}
|
||||
|
||||
// ref: https://fetch.spec.whatwg.org/#body-mixin
|
||||
public async formData(): Promise<FormData> {
|
||||
const formData = new FormData();
|
||||
if (hasHeaderValueOf(this.#contentType, "multipart/form-data")) {
|
||||
const params = getHeaderValueParams(this.#contentType);
|
||||
|
||||
// ref: https://tools.ietf.org/html/rfc2046#section-5.1
|
||||
const boundary = params.get("boundary")!;
|
||||
const body = new Uint8Array(await this.arrayBuffer());
|
||||
const multipartParser = new MultipartParser(body, boundary);
|
||||
|
||||
return multipartParser.parse();
|
||||
} 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");
|
||||
}
|
||||
}
|
||||
|
||||
public async text(): Promise<string> {
|
||||
if (typeof this._bodySource === "string") {
|
||||
return this._bodySource;
|
||||
}
|
||||
|
||||
const ab = await this.arrayBuffer();
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
return decoder.decode(ab);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public async json(): Promise<any> {
|
||||
const raw = await this.text();
|
||||
return JSON.parse(raw);
|
||||
}
|
||||
|
||||
public arrayBuffer(): Promise<ArrayBuffer> {
|
||||
if (isTypedArray(this._bodySource)) {
|
||||
return Promise.resolve(this._bodySource.buffer as ArrayBuffer);
|
||||
} else if (this._bodySource instanceof ArrayBuffer) {
|
||||
return Promise.resolve(this._bodySource);
|
||||
} else if (typeof this._bodySource === "string") {
|
||||
const enc = new TextEncoder();
|
||||
return Promise.resolve(
|
||||
enc.encode(this._bodySource).buffer as ArrayBuffer,
|
||||
);
|
||||
} else if (this._bodySource instanceof ReadableStreamImpl) {
|
||||
return bufferFromStream(this._bodySource.getReader(), this.#size);
|
||||
} else if (
|
||||
this._bodySource instanceof FormData ||
|
||||
this._bodySource instanceof URLSearchParams
|
||||
) {
|
||||
const enc = new TextEncoder();
|
||||
return Promise.resolve(
|
||||
enc.encode(this._bodySource.toString()).buffer as ArrayBuffer,
|
||||
);
|
||||
} else if (!this._bodySource) {
|
||||
return Promise.resolve(new ArrayBuffer(0));
|
||||
}
|
||||
throw new Error(
|
||||
`Body type not yet implemented: ${this._bodySource.constructor.name}`,
|
||||
);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,130 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// Copyright Joyent, Inc. and other Node contributors. MIT license.
|
||||
// Forked from Node's lib/internal/cli_table.js
|
||||
|
||||
import { hasOwnProperty } from "./util.ts";
|
||||
import { stripColor } from "../colors.ts";
|
||||
|
||||
const tableChars = {
|
||||
middleMiddle: "─",
|
||||
rowMiddle: "┼",
|
||||
topRight: "┐",
|
||||
topLeft: "┌",
|
||||
leftMiddle: "├",
|
||||
topMiddle: "┬",
|
||||
bottomRight: "┘",
|
||||
bottomLeft: "└",
|
||||
bottomMiddle: "┴",
|
||||
rightMiddle: "┤",
|
||||
left: "│ ",
|
||||
right: " │",
|
||||
middle: " │ ",
|
||||
};
|
||||
|
||||
function isFullWidthCodePoint(code: number): boolean {
|
||||
// Code points are partially derived from:
|
||||
// http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt
|
||||
return (
|
||||
code >= 0x1100 &&
|
||||
(code <= 0x115f || // Hangul Jamo
|
||||
code === 0x2329 || // LEFT-POINTING ANGLE BRACKET
|
||||
code === 0x232a || // RIGHT-POINTING ANGLE BRACKET
|
||||
// CJK Radicals Supplement .. Enclosed CJK Letters and Months
|
||||
(code >= 0x2e80 && code <= 0x3247 && code !== 0x303f) ||
|
||||
// Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A
|
||||
(code >= 0x3250 && code <= 0x4dbf) ||
|
||||
// CJK Unified Ideographs .. Yi Radicals
|
||||
(code >= 0x4e00 && code <= 0xa4c6) ||
|
||||
// Hangul Jamo Extended-A
|
||||
(code >= 0xa960 && code <= 0xa97c) ||
|
||||
// Hangul Syllables
|
||||
(code >= 0xac00 && code <= 0xd7a3) ||
|
||||
// CJK Compatibility Ideographs
|
||||
(code >= 0xf900 && code <= 0xfaff) ||
|
||||
// Vertical Forms
|
||||
(code >= 0xfe10 && code <= 0xfe19) ||
|
||||
// CJK Compatibility Forms .. Small Form Variants
|
||||
(code >= 0xfe30 && code <= 0xfe6b) ||
|
||||
// Halfwidth and Fullwidth Forms
|
||||
(code >= 0xff01 && code <= 0xff60) ||
|
||||
(code >= 0xffe0 && code <= 0xffe6) ||
|
||||
// Kana Supplement
|
||||
(code >= 0x1b000 && code <= 0x1b001) ||
|
||||
// Enclosed Ideographic Supplement
|
||||
(code >= 0x1f200 && code <= 0x1f251) ||
|
||||
// Miscellaneous Symbols and Pictographs 0x1f300 - 0x1f5ff
|
||||
// Emoticons 0x1f600 - 0x1f64f
|
||||
(code >= 0x1f300 && code <= 0x1f64f) ||
|
||||
// CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane
|
||||
(code >= 0x20000 && code <= 0x3fffd))
|
||||
);
|
||||
}
|
||||
|
||||
function getStringWidth(str: string): number {
|
||||
str = stripColor(str).normalize("NFC");
|
||||
let width = 0;
|
||||
|
||||
for (const ch of str) {
|
||||
width += isFullWidthCodePoint(ch.codePointAt(0)!) ? 2 : 1;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
function renderRow(row: string[], columnWidths: number[]): string {
|
||||
let out = tableChars.left;
|
||||
for (let i = 0; i < row.length; i++) {
|
||||
const cell = row[i];
|
||||
const len = getStringWidth(cell);
|
||||
const needed = (columnWidths[i] - len) / 2;
|
||||
// round(needed) + ceil(needed) will always add up to the amount
|
||||
// of spaces we need while also left justifying the output.
|
||||
out += `${" ".repeat(needed)}${cell}${" ".repeat(Math.ceil(needed))}`;
|
||||
if (i !== row.length - 1) {
|
||||
out += tableChars.middle;
|
||||
}
|
||||
}
|
||||
out += tableChars.right;
|
||||
return out;
|
||||
}
|
||||
|
||||
export function cliTable(head: string[], columns: string[][]): string {
|
||||
const rows: string[][] = [];
|
||||
const columnWidths = head.map((h: string): number => getStringWidth(h));
|
||||
const longestColumn = columns.reduce(
|
||||
(n: number, a: string[]): number => Math.max(n, a.length),
|
||||
0,
|
||||
);
|
||||
|
||||
for (let i = 0; i < head.length; i++) {
|
||||
const column = columns[i];
|
||||
for (let j = 0; j < longestColumn; j++) {
|
||||
if (rows[j] === undefined) {
|
||||
rows[j] = [];
|
||||
}
|
||||
const value = (rows[j][i] = hasOwnProperty(column, j) ? column[j] : "");
|
||||
const width = columnWidths[i] || 0;
|
||||
const counted = getStringWidth(value);
|
||||
columnWidths[i] = Math.max(width, counted);
|
||||
}
|
||||
}
|
||||
|
||||
const divider = columnWidths.map((i: number): string =>
|
||||
tableChars.middleMiddle.repeat(i + 2)
|
||||
);
|
||||
|
||||
let result = `${tableChars.topLeft}${divider.join(tableChars.topMiddle)}` +
|
||||
`${tableChars.topRight}\n${renderRow(head, columnWidths)}\n` +
|
||||
`${tableChars.leftMiddle}${divider.join(tableChars.rowMiddle)}` +
|
||||
`${tableChars.rightMiddle}\n`;
|
||||
|
||||
for (const row of rows) {
|
||||
result += `${renderRow(row, columnWidths)}\n`;
|
||||
}
|
||||
|
||||
result += `${tableChars.bottomLeft}${divider.join(tableChars.bottomMiddle)}` +
|
||||
tableChars.bottomRight;
|
||||
|
||||
return result;
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { EventImpl as Event } from "./event.ts";
|
||||
import { requiredArguments } from "./util.ts";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export class CustomEventImpl<T = any> extends Event implements CustomEvent {
|
||||
readonly #detail: T;
|
||||
|
||||
constructor(type: string, eventInitDict: CustomEventInit<T> = {}) {
|
||||
super(type, eventInitDict);
|
||||
requiredArguments("CustomEvent", arguments.length, 1);
|
||||
const { detail } = eventInitDict;
|
||||
this.#detail = detail as T;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
get detail(): T {
|
||||
return this.#detail;
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag](): string {
|
||||
return "CustomEvent";
|
||||
}
|
||||
}
|
||||
|
||||
Reflect.defineProperty(CustomEventImpl.prototype, "detail", {
|
||||
enumerable: true,
|
||||
});
|
|
@ -1,135 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// This module is based on Bjoern Hoehrmann's DFA UTF-8 decoder.
|
||||
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
||||
//
|
||||
// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// `.apply` can actually take a typed array, though the type system doesn't
|
||||
// really support it, so we have to "hack" it a bit to get past some of the
|
||||
// strict type checks.
|
||||
declare global {
|
||||
interface CallableFunction extends Function {
|
||||
apply<T, R>(
|
||||
this: (this: T, ...args: number[]) => R,
|
||||
thisArg: T,
|
||||
args: Uint16Array,
|
||||
): R;
|
||||
}
|
||||
}
|
||||
|
||||
export function decodeUtf8(
|
||||
input: Uint8Array,
|
||||
fatal: boolean,
|
||||
ignoreBOM: boolean,
|
||||
): string {
|
||||
let outString = "";
|
||||
|
||||
// Prepare a buffer so that we don't have to do a lot of string concats, which
|
||||
// are very slow.
|
||||
const outBufferLength: number = Math.min(1024, input.length);
|
||||
const outBuffer = new Uint16Array(outBufferLength);
|
||||
let outIndex = 0;
|
||||
|
||||
let state = 0;
|
||||
let codepoint = 0;
|
||||
let type: number;
|
||||
|
||||
let i =
|
||||
ignoreBOM && input[0] === 0xef && input[1] === 0xbb && input[2] === 0xbf
|
||||
? 3
|
||||
: 0;
|
||||
|
||||
for (; i < input.length; ++i) {
|
||||
// Encoding error handling
|
||||
if (state === 12 || (state !== 0 && (input[i] & 0xc0) !== 0x80)) {
|
||||
if (fatal) {
|
||||
throw new TypeError(
|
||||
`Decoder error. Invalid byte in sequence at position ${i} in data.`,
|
||||
);
|
||||
}
|
||||
outBuffer[outIndex++] = 0xfffd; // Replacement character
|
||||
if (outIndex === outBufferLength) {
|
||||
outString += String.fromCharCode.apply(null, outBuffer);
|
||||
outIndex = 0;
|
||||
}
|
||||
state = 0;
|
||||
}
|
||||
|
||||
// deno-fmt-ignore
|
||||
type = [
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8
|
||||
][input[i]];
|
||||
codepoint = state !== 0
|
||||
? (input[i] & 0x3f) | (codepoint << 6)
|
||||
: (0xff >> type) & input[i];
|
||||
// deno-fmt-ignore
|
||||
state = [
|
||||
0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
|
||||
12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
|
||||
12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
|
||||
12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
|
||||
12,36,12,12,12,12,12,12,12,12,12,12
|
||||
][state + type];
|
||||
|
||||
if (state !== 0) continue;
|
||||
|
||||
// Add codepoint to buffer (as charcodes for utf-16), and flush buffer to
|
||||
// string if needed.
|
||||
if (codepoint > 0xffff) {
|
||||
outBuffer[outIndex++] = 0xd7c0 + (codepoint >> 10);
|
||||
if (outIndex === outBufferLength) {
|
||||
outString += String.fromCharCode.apply(null, outBuffer);
|
||||
outIndex = 0;
|
||||
}
|
||||
outBuffer[outIndex++] = 0xdc00 | (codepoint & 0x3ff);
|
||||
if (outIndex === outBufferLength) {
|
||||
outString += String.fromCharCode.apply(null, outBuffer);
|
||||
outIndex = 0;
|
||||
}
|
||||
} else {
|
||||
outBuffer[outIndex++] = codepoint;
|
||||
if (outIndex === outBufferLength) {
|
||||
outString += String.fromCharCode.apply(null, outBuffer);
|
||||
outIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a replacement character if we ended in the middle of a sequence or
|
||||
// encountered an invalid code at the end.
|
||||
if (state !== 0) {
|
||||
if (fatal) throw new TypeError(`Decoder error. Unexpected end of data.`);
|
||||
outBuffer[outIndex++] = 0xfffd; // Replacement character
|
||||
}
|
||||
|
||||
// Final flush of buffer
|
||||
outString += String.fromCharCode.apply(null, outBuffer.subarray(0, outIndex));
|
||||
|
||||
return outString;
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import * as blob from "./blob.ts";
|
||||
|
||||
export class DomFileImpl extends blob.DenoBlob implements File {
|
||||
lastModified: number;
|
||||
name: string;
|
||||
|
||||
constructor(
|
||||
fileBits: BlobPart[],
|
||||
fileName: string,
|
||||
options?: FilePropertyBag,
|
||||
) {
|
||||
const { lastModified = Date.now(), ...blobPropertyBag } = options ?? {};
|
||||
super(fileBits, blobPropertyBag);
|
||||
|
||||
// 4.1.2.1 Replace any "/" character (U+002F SOLIDUS)
|
||||
// with a ":" (U + 003A COLON)
|
||||
this.name = String(fileName).replace(/\u002F/g, "\u003A");
|
||||
// 4.1.3.3 If lastModified is not provided, set lastModified to the current
|
||||
// date and time represented in number of milliseconds since the Unix Epoch.
|
||||
this.lastModified = lastModified;
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { requiredArguments } from "./util.ts";
|
||||
import { exposeForTest } from "../internals.ts";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
export interface DomIterable<K, V> {
|
||||
keys(): IterableIterator<K>;
|
||||
values(): IterableIterator<V>;
|
||||
entries(): IterableIterator<[K, V]>;
|
||||
[Symbol.iterator](): IterableIterator<[K, V]>;
|
||||
forEach(
|
||||
callback: (value: V, key: K, parent: this) => void,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
thisArg?: any,
|
||||
): void;
|
||||
}
|
||||
|
||||
export function DomIterableMixin<K, V, TBase extends Constructor>(
|
||||
Base: TBase,
|
||||
dataSymbol: symbol,
|
||||
): TBase & Constructor<DomIterable<K, V>> {
|
||||
// we have to cast `this` as `any` because there is no way to describe the
|
||||
// Base class in a way where the Symbol `dataSymbol` is defined. So the
|
||||
// runtime code works, but we do lose a little bit of type safety.
|
||||
|
||||
// Additionally, we have to not use .keys() nor .values() since the internal
|
||||
// slot differs in type - some have a Map, which yields [K, V] in
|
||||
// Symbol.iterator, and some have an Array, which yields V, in this case
|
||||
// [K, V] too as they are arrays of tuples.
|
||||
|
||||
const DomIterable = class extends Base {
|
||||
*entries(): IterableIterator<[K, V]> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
for (const entry of (this as any)[dataSymbol]) {
|
||||
yield entry;
|
||||
}
|
||||
}
|
||||
|
||||
*keys(): IterableIterator<K> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
for (const [key] of (this as any)[dataSymbol]) {
|
||||
yield key;
|
||||
}
|
||||
}
|
||||
|
||||
*values(): IterableIterator<V> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
for (const [, value] of (this as any)[dataSymbol]) {
|
||||
yield value;
|
||||
}
|
||||
}
|
||||
|
||||
forEach(
|
||||
callbackfn: (value: V, key: K, parent: this) => void,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
thisArg?: any,
|
||||
): void {
|
||||
requiredArguments(
|
||||
`${this.constructor.name}.forEach`,
|
||||
arguments.length,
|
||||
1,
|
||||
);
|
||||
callbackfn = callbackfn.bind(
|
||||
thisArg == null ? globalThis : Object(thisArg),
|
||||
);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
for (const [key, value] of (this as any)[dataSymbol]) {
|
||||
callbackfn(value, key, this);
|
||||
}
|
||||
}
|
||||
|
||||
*[Symbol.iterator](): IterableIterator<[K, V]> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
for (const entry of (this as any)[dataSymbol]) {
|
||||
yield entry;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// we want the Base class name to be the name of the class.
|
||||
Object.defineProperty(DomIterable, "name", {
|
||||
value: Base.name,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
return DomIterable;
|
||||
}
|
||||
|
||||
exposeForTest("DomIterableMixin", DomIterableMixin);
|
318
cli/js/web/dom_types.d.ts
vendored
318
cli/js/web/dom_types.d.ts
vendored
|
@ -1,318 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
/*! ****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
||||
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
*******************************************************************************/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
export type RequestInfo = Request | string;
|
||||
|
||||
export interface ProgressEventInit extends EventInit {
|
||||
lengthComputable?: boolean;
|
||||
loaded?: number;
|
||||
total?: number;
|
||||
}
|
||||
|
||||
export interface UIEventInit extends EventInit {
|
||||
detail?: number;
|
||||
// adjust Window -> Node
|
||||
view?: Node | null;
|
||||
}
|
||||
|
||||
export class UIEvent extends Event {
|
||||
constructor(type: string, eventInitDict?: UIEventInit);
|
||||
readonly detail: number;
|
||||
// adjust Window -> Node
|
||||
readonly view: Node | null;
|
||||
}
|
||||
|
||||
export interface FocusEventInit extends UIEventInit {
|
||||
relatedTarget?: EventTarget | null;
|
||||
}
|
||||
|
||||
export class FocusEvent extends UIEvent {
|
||||
constructor(type: string, eventInitDict?: FocusEventInit);
|
||||
readonly relatedTarget: EventTarget | null;
|
||||
}
|
||||
|
||||
export interface EventModifierInit extends UIEventInit {
|
||||
altKey?: boolean;
|
||||
ctrlKey?: boolean;
|
||||
metaKey?: boolean;
|
||||
modifierAltGraph?: boolean;
|
||||
modifierCapsLock?: boolean;
|
||||
modifierFn?: boolean;
|
||||
modifierFnLock?: boolean;
|
||||
modifierHyper?: boolean;
|
||||
modifierNumLock?: boolean;
|
||||
modifierScrollLock?: boolean;
|
||||
modifierSuper?: boolean;
|
||||
modifierSymbol?: boolean;
|
||||
modifierSymbolLock?: boolean;
|
||||
shiftKey?: boolean;
|
||||
}
|
||||
|
||||
export interface MouseEventInit extends EventModifierInit {
|
||||
button?: number;
|
||||
buttons?: number;
|
||||
clientX?: number;
|
||||
clientY?: number;
|
||||
movementX?: number;
|
||||
movementY?: number;
|
||||
relatedTarget?: EventTarget | null;
|
||||
screenX?: number;
|
||||
screenY?: number;
|
||||
}
|
||||
|
||||
export class MouseEvent extends UIEvent {
|
||||
constructor(type: string, eventInitDict?: MouseEventInit);
|
||||
readonly altKey: boolean;
|
||||
readonly button: number;
|
||||
readonly buttons: number;
|
||||
readonly clientX: number;
|
||||
readonly clientY: number;
|
||||
readonly ctrlKey: boolean;
|
||||
readonly metaKey: boolean;
|
||||
readonly movementX: number;
|
||||
readonly movementY: number;
|
||||
readonly offsetX: number;
|
||||
readonly offsetY: number;
|
||||
readonly pageX: number;
|
||||
readonly pageY: number;
|
||||
readonly relatedTarget: EventTarget | null;
|
||||
readonly screenX: number;
|
||||
readonly screenY: number;
|
||||
readonly shiftKey: boolean;
|
||||
readonly x: number;
|
||||
readonly y: number;
|
||||
getModifierState(keyArg: string): boolean;
|
||||
}
|
||||
|
||||
interface GetRootNodeOptions {
|
||||
composed?: boolean;
|
||||
}
|
||||
|
||||
export class Node extends EventTarget {
|
||||
readonly baseURI: string;
|
||||
readonly childNodes: NodeListOf<ChildNode>;
|
||||
readonly firstChild: ChildNode | null;
|
||||
readonly isConnected: boolean;
|
||||
readonly lastChild: ChildNode | null;
|
||||
readonly nextSibling: ChildNode | null;
|
||||
readonly nodeName: string;
|
||||
readonly nodeType: number;
|
||||
nodeValue: string | null;
|
||||
// adjusted: Document -> Node
|
||||
readonly ownerDocument: Node | null;
|
||||
// adjusted: HTMLElement -> Node
|
||||
readonly parentElement: Node | null;
|
||||
readonly parentNode: (Node & ParentNode) | null;
|
||||
readonly previousSibling: ChildNode | null;
|
||||
textContent: string | null;
|
||||
appendChild<T extends Node>(newChild: T): T;
|
||||
cloneNode(deep?: boolean): Node;
|
||||
compareDocumentPosition(other: Node): number;
|
||||
contains(other: Node | null): boolean;
|
||||
getRootNode(options?: GetRootNodeOptions): Node;
|
||||
hasChildNodes(): boolean;
|
||||
insertBefore<T extends Node>(newChild: T, refChild: Node | null): T;
|
||||
isDefaultNamespace(namespace: string | null): boolean;
|
||||
isEqualNode(otherNode: Node | null): boolean;
|
||||
isSameNode(otherNode: Node | null): boolean;
|
||||
lookupNamespaceURI(prefix: string | null): string | null;
|
||||
lookupPrefix(namespace: string | null): string | null;
|
||||
normalize(): void;
|
||||
removeChild<T extends Node>(oldChild: T): T;
|
||||
replaceChild<T extends Node>(newChild: Node, oldChild: T): T;
|
||||
readonly ATTRIBUTE_NODE: number;
|
||||
readonly CDATA_SECTION_NODE: number;
|
||||
readonly COMMENT_NODE: number;
|
||||
readonly DOCUMENT_FRAGMENT_NODE: number;
|
||||
readonly DOCUMENT_NODE: number;
|
||||
readonly DOCUMENT_POSITION_CONTAINED_BY: number;
|
||||
readonly DOCUMENT_POSITION_CONTAINS: number;
|
||||
readonly DOCUMENT_POSITION_DISCONNECTED: number;
|
||||
readonly DOCUMENT_POSITION_FOLLOWING: number;
|
||||
readonly DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: number;
|
||||
readonly DOCUMENT_POSITION_PRECEDING: number;
|
||||
readonly DOCUMENT_TYPE_NODE: number;
|
||||
readonly ELEMENT_NODE: number;
|
||||
readonly ENTITY_NODE: number;
|
||||
readonly ENTITY_REFERENCE_NODE: number;
|
||||
readonly NOTATION_NODE: number;
|
||||
readonly PROCESSING_INSTRUCTION_NODE: number;
|
||||
readonly TEXT_NODE: number;
|
||||
static readonly ATTRIBUTE_NODE: number;
|
||||
static readonly CDATA_SECTION_NODE: number;
|
||||
static readonly COMMENT_NODE: number;
|
||||
static readonly DOCUMENT_FRAGMENT_NODE: number;
|
||||
static readonly DOCUMENT_NODE: number;
|
||||
static readonly DOCUMENT_POSITION_CONTAINED_BY: number;
|
||||
static readonly DOCUMENT_POSITION_CONTAINS: number;
|
||||
static readonly DOCUMENT_POSITION_DISCONNECTED: number;
|
||||
static readonly DOCUMENT_POSITION_FOLLOWING: number;
|
||||
static readonly DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: number;
|
||||
static readonly DOCUMENT_POSITION_PRECEDING: number;
|
||||
static readonly DOCUMENT_TYPE_NODE: number;
|
||||
static readonly ELEMENT_NODE: number;
|
||||
static readonly ENTITY_NODE: number;
|
||||
static readonly ENTITY_REFERENCE_NODE: number;
|
||||
static readonly NOTATION_NODE: number;
|
||||
static readonly PROCESSING_INSTRUCTION_NODE: number;
|
||||
static readonly TEXT_NODE: number;
|
||||
}
|
||||
|
||||
interface Slotable {
|
||||
// adjusted: HTMLSlotElement -> Node
|
||||
readonly assignedSlot: Node | null;
|
||||
}
|
||||
|
||||
interface ChildNode extends Node {
|
||||
after(...nodes: Array<Node | string>): void;
|
||||
before(...nodes: Array<Node | string>): void;
|
||||
remove(): void;
|
||||
replaceWith(...nodes: Array<Node | string>): void;
|
||||
}
|
||||
|
||||
interface ParentNode {
|
||||
readonly childElementCount: number;
|
||||
// not currently supported
|
||||
// readonly children: HTMLCollection;
|
||||
// adjusted: Element -> Node
|
||||
readonly firstElementChild: Node | null;
|
||||
// adjusted: Element -> Node
|
||||
readonly lastElementChild: Node | null;
|
||||
append(...nodes: Array<Node | string>): void;
|
||||
prepend(...nodes: Array<Node | string>): void;
|
||||
// not currently supported
|
||||
// querySelector<K extends keyof HTMLElementTagNameMap>(
|
||||
// selectors: K,
|
||||
// ): HTMLElementTagNameMap[K] | null;
|
||||
// querySelector<K extends keyof SVGElementTagNameMap>(
|
||||
// selectors: K,
|
||||
// ): SVGElementTagNameMap[K] | null;
|
||||
// querySelector<E extends Element = Element>(selectors: string): E | null;
|
||||
// querySelectorAll<K extends keyof HTMLElementTagNameMap>(
|
||||
// selectors: K,
|
||||
// ): NodeListOf<HTMLElementTagNameMap[K]>;
|
||||
// querySelectorAll<K extends keyof SVGElementTagNameMap>(
|
||||
// selectors: K,
|
||||
// ): NodeListOf<SVGElementTagNameMap[K]>;
|
||||
// querySelectorAll<E extends Element = Element>(
|
||||
// selectors: string,
|
||||
// ): NodeListOf<E>;
|
||||
}
|
||||
|
||||
interface NodeList {
|
||||
readonly length: number;
|
||||
item(index: number): Node | null;
|
||||
forEach(
|
||||
callbackfn: (value: Node, key: number, parent: NodeList) => void,
|
||||
thisArg?: any,
|
||||
): void;
|
||||
[index: number]: Node;
|
||||
[Symbol.iterator](): IterableIterator<Node>;
|
||||
entries(): IterableIterator<[number, Node]>;
|
||||
keys(): IterableIterator<number>;
|
||||
values(): IterableIterator<Node>;
|
||||
}
|
||||
|
||||
interface NodeListOf<TNode extends Node> extends NodeList {
|
||||
length: number;
|
||||
item(index: number): TNode;
|
||||
forEach(
|
||||
callbackfn: (value: TNode, key: number, parent: NodeListOf<TNode>) => void,
|
||||
thisArg?: any,
|
||||
): void;
|
||||
[index: number]: TNode;
|
||||
[Symbol.iterator](): IterableIterator<TNode>;
|
||||
entries(): IterableIterator<[number, TNode]>;
|
||||
keys(): IterableIterator<number>;
|
||||
values(): IterableIterator<TNode>;
|
||||
}
|
||||
|
||||
export interface Body {
|
||||
readonly body: ReadableStream<Uint8Array> | null;
|
||||
readonly bodyUsed: boolean;
|
||||
arrayBuffer(): Promise<ArrayBuffer>;
|
||||
blob(): Promise<Blob>;
|
||||
formData(): Promise<FormData>;
|
||||
json(): Promise<any>;
|
||||
text(): Promise<string>;
|
||||
}
|
||||
|
||||
export interface RequestInit {
|
||||
body?: BodyInit | null;
|
||||
cache?: RequestCache;
|
||||
credentials?: RequestCredentials;
|
||||
headers?: HeadersInit;
|
||||
integrity?: string;
|
||||
keepalive?: boolean;
|
||||
method?: string;
|
||||
mode?: RequestMode;
|
||||
redirect?: RequestRedirect;
|
||||
referrer?: string;
|
||||
referrerPolicy?: ReferrerPolicy;
|
||||
signal?: AbortSignal | null;
|
||||
window?: any;
|
||||
}
|
||||
|
||||
export interface ResponseInit {
|
||||
headers?: HeadersInit;
|
||||
status?: number;
|
||||
statusText?: string;
|
||||
}
|
||||
|
||||
export interface Request extends Body {
|
||||
readonly cache?: RequestCache;
|
||||
readonly credentials?: RequestCredentials;
|
||||
readonly destination?: RequestDestination;
|
||||
readonly headers: Headers;
|
||||
readonly integrity?: string;
|
||||
readonly isHistoryNavigation?: boolean;
|
||||
readonly isReloadNavigation?: boolean;
|
||||
readonly keepalive?: boolean;
|
||||
readonly method: string;
|
||||
readonly mode?: RequestMode;
|
||||
readonly redirect?: RequestRedirect;
|
||||
readonly referrer?: string;
|
||||
readonly referrerPolicy?: ReferrerPolicy;
|
||||
readonly signal?: AbortSignal;
|
||||
readonly url: string;
|
||||
clone(): Request;
|
||||
}
|
||||
|
||||
export interface RequestConstructor {
|
||||
new (input: RequestInfo, init?: RequestInit): Request;
|
||||
prototype: Request;
|
||||
}
|
||||
|
||||
export interface Response extends Body {
|
||||
readonly headers: Headers;
|
||||
readonly ok: boolean;
|
||||
readonly redirected: boolean;
|
||||
readonly status: number;
|
||||
readonly statusText: string;
|
||||
readonly type: ResponseType;
|
||||
readonly url: string;
|
||||
clone(): Response;
|
||||
}
|
||||
|
||||
export interface ResponseConstructor {
|
||||
prototype: Response;
|
||||
new (body?: BodyInit | null, init?: ResponseInit): Response;
|
||||
error(): Response;
|
||||
redirect(url: string, status?: number): Response;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
export function getDOMStringList(arr: string[]): DOMStringList {
|
||||
Object.defineProperties(arr, {
|
||||
contains: {
|
||||
value(searchElement: string): boolean {
|
||||
return arr.includes(searchElement);
|
||||
},
|
||||
enumerable: true,
|
||||
},
|
||||
item: {
|
||||
value(idx: number): string | null {
|
||||
return idx in arr ? arr[idx] : null;
|
||||
},
|
||||
},
|
||||
});
|
||||
return arr as string[] & DOMStringList;
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { EventImpl as Event } from "./event.ts";
|
||||
import { defineEnumerableProps } from "./util.ts";
|
||||
|
||||
export class ErrorEventImpl extends Event implements ErrorEvent {
|
||||
readonly #message: string;
|
||||
readonly #filename: string;
|
||||
readonly #lineno: number;
|
||||
readonly #colno: number;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
readonly #error: any;
|
||||
|
||||
get message(): string {
|
||||
return this.#message;
|
||||
}
|
||||
get filename(): string {
|
||||
return this.#filename;
|
||||
}
|
||||
get lineno(): number {
|
||||
return this.#lineno;
|
||||
}
|
||||
get colno(): number {
|
||||
return this.#colno;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
get error(): any {
|
||||
return this.#error;
|
||||
}
|
||||
|
||||
constructor(
|
||||
type: string,
|
||||
{
|
||||
bubbles,
|
||||
cancelable,
|
||||
composed,
|
||||
message = "",
|
||||
filename = "",
|
||||
lineno = 0,
|
||||
colno = 0,
|
||||
error = null,
|
||||
}: ErrorEventInit = {},
|
||||
) {
|
||||
super(type, {
|
||||
bubbles: bubbles,
|
||||
cancelable: cancelable,
|
||||
composed: composed,
|
||||
});
|
||||
|
||||
this.#message = message;
|
||||
this.#filename = filename;
|
||||
this.#lineno = lineno;
|
||||
this.#colno = colno;
|
||||
this.#error = error;
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag](): string {
|
||||
return "ErrorEvent";
|
||||
}
|
||||
}
|
||||
|
||||
defineEnumerableProps(ErrorEventImpl, [
|
||||
"message",
|
||||
"filename",
|
||||
"lineno",
|
||||
"colno",
|
||||
"error",
|
||||
]);
|
|
@ -1,406 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import type * as domTypes from "./dom_types.d.ts";
|
||||
import { defineEnumerableProps, requiredArguments } from "./util.ts";
|
||||
import { assert } from "../util.ts";
|
||||
|
||||
/** Stores a non-accessible view of the event path which is used internally in
|
||||
* the logic for determining the path of an event. */
|
||||
export interface EventPath {
|
||||
item: EventTarget;
|
||||
itemInShadowTree: boolean;
|
||||
relatedTarget: EventTarget | null;
|
||||
rootOfClosedTree: boolean;
|
||||
slotInClosedTree: boolean;
|
||||
target: EventTarget | null;
|
||||
touchTargetList: EventTarget[];
|
||||
}
|
||||
|
||||
interface EventAttributes {
|
||||
type: string;
|
||||
bubbles: boolean;
|
||||
cancelable: boolean;
|
||||
composed: boolean;
|
||||
currentTarget: EventTarget | null;
|
||||
eventPhase: number;
|
||||
target: EventTarget | null;
|
||||
timeStamp: number;
|
||||
}
|
||||
|
||||
interface EventData {
|
||||
dispatched: boolean;
|
||||
inPassiveListener: boolean;
|
||||
isTrusted: boolean;
|
||||
path: EventPath[];
|
||||
stopImmediatePropagation: boolean;
|
||||
}
|
||||
|
||||
const eventData = new WeakMap<Event, EventData>();
|
||||
|
||||
// accessors for non runtime visible data
|
||||
|
||||
export function getDispatched(event: Event): boolean {
|
||||
return Boolean(eventData.get(event)?.dispatched);
|
||||
}
|
||||
|
||||
export function getPath(event: Event): EventPath[] {
|
||||
return eventData.get(event)?.path ?? [];
|
||||
}
|
||||
|
||||
export function getStopImmediatePropagation(event: Event): boolean {
|
||||
return Boolean(eventData.get(event)?.stopImmediatePropagation);
|
||||
}
|
||||
|
||||
export function setCurrentTarget(
|
||||
event: Event,
|
||||
value: EventTarget | null,
|
||||
): void {
|
||||
(event as EventImpl).currentTarget = value;
|
||||
}
|
||||
|
||||
export function setDispatched(event: Event, value: boolean): void {
|
||||
const data = eventData.get(event as Event);
|
||||
if (data) {
|
||||
data.dispatched = value;
|
||||
}
|
||||
}
|
||||
|
||||
export function setEventPhase(event: Event, value: number): void {
|
||||
(event as EventImpl).eventPhase = value;
|
||||
}
|
||||
|
||||
export function setInPassiveListener(event: Event, value: boolean): void {
|
||||
const data = eventData.get(event as Event);
|
||||
if (data) {
|
||||
data.inPassiveListener = value;
|
||||
}
|
||||
}
|
||||
|
||||
export function setPath(event: Event, value: EventPath[]): void {
|
||||
const data = eventData.get(event as Event);
|
||||
if (data) {
|
||||
data.path = value;
|
||||
}
|
||||
}
|
||||
|
||||
export function setRelatedTarget<T extends Event>(
|
||||
event: T,
|
||||
value: EventTarget | null,
|
||||
): void {
|
||||
if ("relatedTarget" in event) {
|
||||
(event as T & {
|
||||
relatedTarget: EventTarget | null;
|
||||
}).relatedTarget = value;
|
||||
}
|
||||
}
|
||||
|
||||
export function setTarget(event: Event, value: EventTarget | null): void {
|
||||
(event as EventImpl).target = value;
|
||||
}
|
||||
|
||||
export function setStopImmediatePropagation(
|
||||
event: Event,
|
||||
value: boolean,
|
||||
): void {
|
||||
const data = eventData.get(event as Event);
|
||||
if (data) {
|
||||
data.stopImmediatePropagation = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Type guards that widen the event type
|
||||
|
||||
export function hasRelatedTarget(
|
||||
event: Event,
|
||||
): event is domTypes.FocusEvent | domTypes.MouseEvent {
|
||||
return "relatedTarget" in event;
|
||||
}
|
||||
|
||||
function isTrusted(this: Event): boolean {
|
||||
return eventData.get(this)!.isTrusted;
|
||||
}
|
||||
|
||||
export class EventImpl implements Event {
|
||||
// The default value is `false`.
|
||||
// Use `defineProperty` to define on each instance, NOT on the prototype.
|
||||
isTrusted!: boolean;
|
||||
|
||||
#canceledFlag = false;
|
||||
#stopPropagationFlag = false;
|
||||
#attributes: EventAttributes;
|
||||
|
||||
constructor(type: string, eventInitDict: EventInit = {}) {
|
||||
requiredArguments("Event", arguments.length, 1);
|
||||
type = String(type);
|
||||
this.#attributes = {
|
||||
type,
|
||||
bubbles: eventInitDict.bubbles ?? false,
|
||||
cancelable: eventInitDict.cancelable ?? false,
|
||||
composed: eventInitDict.composed ?? false,
|
||||
currentTarget: null,
|
||||
eventPhase: Event.NONE,
|
||||
target: null,
|
||||
timeStamp: Date.now(),
|
||||
};
|
||||
eventData.set(this, {
|
||||
dispatched: false,
|
||||
inPassiveListener: false,
|
||||
isTrusted: false,
|
||||
path: [],
|
||||
stopImmediatePropagation: false,
|
||||
});
|
||||
Reflect.defineProperty(this, "isTrusted", {
|
||||
enumerable: true,
|
||||
get: isTrusted,
|
||||
});
|
||||
}
|
||||
|
||||
get bubbles(): boolean {
|
||||
return this.#attributes.bubbles;
|
||||
}
|
||||
|
||||
get cancelBubble(): boolean {
|
||||
return this.#stopPropagationFlag;
|
||||
}
|
||||
|
||||
set cancelBubble(value: boolean) {
|
||||
this.#stopPropagationFlag = value;
|
||||
}
|
||||
|
||||
get cancelable(): boolean {
|
||||
return this.#attributes.cancelable;
|
||||
}
|
||||
|
||||
get composed(): boolean {
|
||||
return this.#attributes.composed;
|
||||
}
|
||||
|
||||
get currentTarget(): EventTarget | null {
|
||||
return this.#attributes.currentTarget;
|
||||
}
|
||||
|
||||
set currentTarget(value: EventTarget | null) {
|
||||
this.#attributes = {
|
||||
type: this.type,
|
||||
bubbles: this.bubbles,
|
||||
cancelable: this.cancelable,
|
||||
composed: this.composed,
|
||||
currentTarget: value,
|
||||
eventPhase: this.eventPhase,
|
||||
target: this.target,
|
||||
timeStamp: this.timeStamp,
|
||||
};
|
||||
}
|
||||
|
||||
get defaultPrevented(): boolean {
|
||||
return this.#canceledFlag;
|
||||
}
|
||||
|
||||
get eventPhase(): number {
|
||||
return this.#attributes.eventPhase;
|
||||
}
|
||||
|
||||
set eventPhase(value: number) {
|
||||
this.#attributes = {
|
||||
type: this.type,
|
||||
bubbles: this.bubbles,
|
||||
cancelable: this.cancelable,
|
||||
composed: this.composed,
|
||||
currentTarget: this.currentTarget,
|
||||
eventPhase: value,
|
||||
target: this.target,
|
||||
timeStamp: this.timeStamp,
|
||||
};
|
||||
}
|
||||
|
||||
get initialized(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
get target(): EventTarget | null {
|
||||
return this.#attributes.target;
|
||||
}
|
||||
|
||||
set target(value: EventTarget | null) {
|
||||
this.#attributes = {
|
||||
type: this.type,
|
||||
bubbles: this.bubbles,
|
||||
cancelable: this.cancelable,
|
||||
composed: this.composed,
|
||||
currentTarget: this.currentTarget,
|
||||
eventPhase: this.eventPhase,
|
||||
target: value,
|
||||
timeStamp: this.timeStamp,
|
||||
};
|
||||
}
|
||||
|
||||
get timeStamp(): number {
|
||||
return this.#attributes.timeStamp;
|
||||
}
|
||||
|
||||
get type(): string {
|
||||
return this.#attributes.type;
|
||||
}
|
||||
|
||||
composedPath(): EventTarget[] {
|
||||
const path = eventData.get(this)!.path;
|
||||
if (path.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
assert(this.currentTarget);
|
||||
const composedPath: EventPath[] = [
|
||||
{
|
||||
item: this.currentTarget,
|
||||
itemInShadowTree: false,
|
||||
relatedTarget: null,
|
||||
rootOfClosedTree: false,
|
||||
slotInClosedTree: false,
|
||||
target: null,
|
||||
touchTargetList: [],
|
||||
},
|
||||
];
|
||||
|
||||
let currentTargetIndex = 0;
|
||||
let currentTargetHiddenSubtreeLevel = 0;
|
||||
|
||||
for (let index = path.length - 1; index >= 0; index--) {
|
||||
const { item, rootOfClosedTree, slotInClosedTree } = path[index];
|
||||
|
||||
if (rootOfClosedTree) {
|
||||
currentTargetHiddenSubtreeLevel++;
|
||||
}
|
||||
|
||||
if (item === this.currentTarget) {
|
||||
currentTargetIndex = index;
|
||||
break;
|
||||
}
|
||||
|
||||
if (slotInClosedTree) {
|
||||
currentTargetHiddenSubtreeLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
let currentHiddenLevel = currentTargetHiddenSubtreeLevel;
|
||||
let maxHiddenLevel = currentTargetHiddenSubtreeLevel;
|
||||
|
||||
for (let i = currentTargetIndex - 1; i >= 0; i--) {
|
||||
const { item, rootOfClosedTree, slotInClosedTree } = path[i];
|
||||
|
||||
if (rootOfClosedTree) {
|
||||
currentHiddenLevel++;
|
||||
}
|
||||
|
||||
if (currentHiddenLevel <= maxHiddenLevel) {
|
||||
composedPath.unshift({
|
||||
item,
|
||||
itemInShadowTree: false,
|
||||
relatedTarget: null,
|
||||
rootOfClosedTree: false,
|
||||
slotInClosedTree: false,
|
||||
target: null,
|
||||
touchTargetList: [],
|
||||
});
|
||||
}
|
||||
|
||||
if (slotInClosedTree) {
|
||||
currentHiddenLevel--;
|
||||
|
||||
if (currentHiddenLevel < maxHiddenLevel) {
|
||||
maxHiddenLevel = currentHiddenLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentHiddenLevel = currentTargetHiddenSubtreeLevel;
|
||||
maxHiddenLevel = currentTargetHiddenSubtreeLevel;
|
||||
|
||||
for (let index = currentTargetIndex + 1; index < path.length; index++) {
|
||||
const { item, rootOfClosedTree, slotInClosedTree } = path[index];
|
||||
|
||||
if (slotInClosedTree) {
|
||||
currentHiddenLevel++;
|
||||
}
|
||||
|
||||
if (currentHiddenLevel <= maxHiddenLevel) {
|
||||
composedPath.push({
|
||||
item,
|
||||
itemInShadowTree: false,
|
||||
relatedTarget: null,
|
||||
rootOfClosedTree: false,
|
||||
slotInClosedTree: false,
|
||||
target: null,
|
||||
touchTargetList: [],
|
||||
});
|
||||
}
|
||||
|
||||
if (rootOfClosedTree) {
|
||||
currentHiddenLevel--;
|
||||
|
||||
if (currentHiddenLevel < maxHiddenLevel) {
|
||||
maxHiddenLevel = currentHiddenLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
return composedPath.map((p) => p.item);
|
||||
}
|
||||
|
||||
preventDefault(): void {
|
||||
if (this.cancelable && !eventData.get(this)!.inPassiveListener) {
|
||||
this.#canceledFlag = true;
|
||||
}
|
||||
}
|
||||
|
||||
stopPropagation(): void {
|
||||
this.#stopPropagationFlag = true;
|
||||
}
|
||||
|
||||
stopImmediatePropagation(): void {
|
||||
this.#stopPropagationFlag = true;
|
||||
eventData.get(this)!.stopImmediatePropagation = true;
|
||||
}
|
||||
|
||||
get NONE(): number {
|
||||
return Event.NONE;
|
||||
}
|
||||
|
||||
get CAPTURING_PHASE(): number {
|
||||
return Event.CAPTURING_PHASE;
|
||||
}
|
||||
|
||||
get AT_TARGET(): number {
|
||||
return Event.AT_TARGET;
|
||||
}
|
||||
|
||||
get BUBBLING_PHASE(): number {
|
||||
return Event.BUBBLING_PHASE;
|
||||
}
|
||||
|
||||
static get NONE(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static get CAPTURING_PHASE(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static get AT_TARGET(): number {
|
||||
return 2;
|
||||
}
|
||||
|
||||
static get BUBBLING_PHASE(): number {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
defineEnumerableProps(EventImpl, [
|
||||
"bubbles",
|
||||
"cancelable",
|
||||
"composed",
|
||||
"currentTarget",
|
||||
"defaultPrevented",
|
||||
"eventPhase",
|
||||
"target",
|
||||
"timeStamp",
|
||||
"type",
|
||||
]);
|
|
@ -1,588 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// This module follows most of the WHATWG Living Standard for the DOM logic.
|
||||
// Many parts of the DOM are not implemented in Deno, but the logic for those
|
||||
// parts still exists. This means you will observe a lot of strange structures
|
||||
// and impossible logic branches based on what Deno currently supports.
|
||||
|
||||
import { DOMExceptionImpl as DOMException } from "./dom_exception.ts";
|
||||
import type * as domTypes from "./dom_types.d.ts";
|
||||
import {
|
||||
EventImpl as Event,
|
||||
EventPath,
|
||||
getDispatched,
|
||||
getPath,
|
||||
getStopImmediatePropagation,
|
||||
hasRelatedTarget,
|
||||
setCurrentTarget,
|
||||
setDispatched,
|
||||
setEventPhase,
|
||||
setInPassiveListener,
|
||||
setPath,
|
||||
setRelatedTarget,
|
||||
setStopImmediatePropagation,
|
||||
setTarget,
|
||||
} from "./event.ts";
|
||||
import { defineEnumerableProps, requiredArguments } from "./util.ts";
|
||||
|
||||
// This is currently the only node type we are using, so instead of implementing
|
||||
// the whole of the Node interface at the moment, this just gives us the one
|
||||
// value to power the standards based logic
|
||||
const DOCUMENT_FRAGMENT_NODE = 11;
|
||||
|
||||
// DOM Logic Helper functions and type guards
|
||||
|
||||
/** Get the parent node, for event targets that have a parent.
|
||||
*
|
||||
* Ref: https://dom.spec.whatwg.org/#get-the-parent */
|
||||
function getParent(eventTarget: EventTarget): EventTarget | null {
|
||||
return isNode(eventTarget) ? eventTarget.parentNode : null;
|
||||
}
|
||||
|
||||
function getRoot(eventTarget: EventTarget): EventTarget | null {
|
||||
return isNode(eventTarget)
|
||||
? eventTarget.getRootNode({ composed: true })
|
||||
: null;
|
||||
}
|
||||
|
||||
function isNode<T extends EventTarget>(
|
||||
eventTarget: T | null,
|
||||
): eventTarget is T & domTypes.Node {
|
||||
return Boolean(eventTarget && "nodeType" in eventTarget);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor
|
||||
function isShadowInclusiveAncestor(
|
||||
ancestor: EventTarget | null,
|
||||
node: EventTarget | null,
|
||||
): boolean {
|
||||
while (isNode(node)) {
|
||||
if (node === ancestor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isShadowRoot(node)) {
|
||||
node = node && getHost(node);
|
||||
} else {
|
||||
node = getParent(node);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isShadowRoot(nodeImpl: EventTarget | null): boolean {
|
||||
return Boolean(
|
||||
nodeImpl &&
|
||||
isNode(nodeImpl) &&
|
||||
nodeImpl.nodeType === DOCUMENT_FRAGMENT_NODE &&
|
||||
getHost(nodeImpl) != null,
|
||||
);
|
||||
}
|
||||
|
||||
function isSlotable<T extends EventTarget>(
|
||||
nodeImpl: T | null,
|
||||
): nodeImpl is T & domTypes.Node & domTypes.Slotable {
|
||||
return Boolean(isNode(nodeImpl) && "assignedSlot" in nodeImpl);
|
||||
}
|
||||
|
||||
// DOM Logic functions
|
||||
|
||||
/** Append a path item to an event's path.
|
||||
*
|
||||
* Ref: https://dom.spec.whatwg.org/#concept-event-path-append
|
||||
*/
|
||||
function appendToEventPath(
|
||||
eventImpl: Event,
|
||||
target: EventTarget,
|
||||
targetOverride: EventTarget | null,
|
||||
relatedTarget: EventTarget | null,
|
||||
touchTargets: EventTarget[],
|
||||
slotInClosedTree: boolean,
|
||||
): void {
|
||||
const itemInShadowTree = isNode(target) && isShadowRoot(getRoot(target));
|
||||
const rootOfClosedTree = isShadowRoot(target) && getMode(target) === "closed";
|
||||
|
||||
getPath(eventImpl).push({
|
||||
item: target,
|
||||
itemInShadowTree,
|
||||
target: targetOverride,
|
||||
relatedTarget,
|
||||
touchTargetList: touchTargets,
|
||||
rootOfClosedTree,
|
||||
slotInClosedTree,
|
||||
});
|
||||
}
|
||||
|
||||
function dispatch(
|
||||
targetImpl: EventTarget,
|
||||
eventImpl: Event,
|
||||
targetOverride?: EventTarget,
|
||||
): boolean {
|
||||
let clearTargets = false;
|
||||
let activationTarget: EventTarget | null = null;
|
||||
|
||||
setDispatched(eventImpl, true);
|
||||
|
||||
targetOverride = targetOverride ?? targetImpl;
|
||||
const eventRelatedTarget = hasRelatedTarget(eventImpl)
|
||||
? eventImpl.relatedTarget
|
||||
: null;
|
||||
let relatedTarget = retarget(eventRelatedTarget, targetImpl);
|
||||
|
||||
if (targetImpl !== relatedTarget || targetImpl === eventRelatedTarget) {
|
||||
const touchTargets: EventTarget[] = [];
|
||||
|
||||
appendToEventPath(
|
||||
eventImpl,
|
||||
targetImpl,
|
||||
targetOverride,
|
||||
relatedTarget,
|
||||
touchTargets,
|
||||
false,
|
||||
);
|
||||
|
||||
const isActivationEvent = eventImpl.type === "click";
|
||||
|
||||
if (isActivationEvent && getHasActivationBehavior(targetImpl)) {
|
||||
activationTarget = targetImpl;
|
||||
}
|
||||
|
||||
let slotInClosedTree = false;
|
||||
let slotable = isSlotable(targetImpl) && getAssignedSlot(targetImpl)
|
||||
? targetImpl
|
||||
: null;
|
||||
let parent = getParent(targetImpl);
|
||||
|
||||
// Populate event path
|
||||
// https://dom.spec.whatwg.org/#event-path
|
||||
while (parent !== null) {
|
||||
if (slotable !== null) {
|
||||
slotable = null;
|
||||
|
||||
const parentRoot = getRoot(parent);
|
||||
if (
|
||||
isShadowRoot(parentRoot) &&
|
||||
parentRoot &&
|
||||
getMode(parentRoot) === "closed"
|
||||
) {
|
||||
slotInClosedTree = true;
|
||||
}
|
||||
}
|
||||
|
||||
relatedTarget = retarget(eventRelatedTarget, parent);
|
||||
|
||||
if (
|
||||
isNode(parent) &&
|
||||
isShadowInclusiveAncestor(getRoot(targetImpl), parent)
|
||||
) {
|
||||
appendToEventPath(
|
||||
eventImpl,
|
||||
parent,
|
||||
null,
|
||||
relatedTarget,
|
||||
touchTargets,
|
||||
slotInClosedTree,
|
||||
);
|
||||
} else if (parent === relatedTarget) {
|
||||
parent = null;
|
||||
} else {
|
||||
targetImpl = parent;
|
||||
|
||||
if (
|
||||
isActivationEvent &&
|
||||
activationTarget === null &&
|
||||
getHasActivationBehavior(targetImpl)
|
||||
) {
|
||||
activationTarget = targetImpl;
|
||||
}
|
||||
|
||||
appendToEventPath(
|
||||
eventImpl,
|
||||
parent,
|
||||
targetImpl,
|
||||
relatedTarget,
|
||||
touchTargets,
|
||||
slotInClosedTree,
|
||||
);
|
||||
}
|
||||
|
||||
if (parent !== null) {
|
||||
parent = getParent(parent);
|
||||
}
|
||||
|
||||
slotInClosedTree = false;
|
||||
}
|
||||
|
||||
let clearTargetsTupleIndex = -1;
|
||||
const path = getPath(eventImpl);
|
||||
for (
|
||||
let i = path.length - 1;
|
||||
i >= 0 && clearTargetsTupleIndex === -1;
|
||||
i--
|
||||
) {
|
||||
if (path[i].target !== null) {
|
||||
clearTargetsTupleIndex = i;
|
||||
}
|
||||
}
|
||||
const clearTargetsTuple = path[clearTargetsTupleIndex];
|
||||
|
||||
clearTargets = (isNode(clearTargetsTuple.target) &&
|
||||
isShadowRoot(getRoot(clearTargetsTuple.target))) ||
|
||||
(isNode(clearTargetsTuple.relatedTarget) &&
|
||||
isShadowRoot(getRoot(clearTargetsTuple.relatedTarget)));
|
||||
|
||||
setEventPhase(eventImpl, Event.CAPTURING_PHASE);
|
||||
|
||||
for (let i = path.length - 1; i >= 0; --i) {
|
||||
const tuple = path[i];
|
||||
|
||||
if (tuple.target === null) {
|
||||
invokeEventListeners(tuple, eventImpl);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
const tuple = path[i];
|
||||
|
||||
if (tuple.target !== null) {
|
||||
setEventPhase(eventImpl, Event.AT_TARGET);
|
||||
} else {
|
||||
setEventPhase(eventImpl, Event.BUBBLING_PHASE);
|
||||
}
|
||||
|
||||
if (
|
||||
(eventImpl.eventPhase === Event.BUBBLING_PHASE && eventImpl.bubbles) ||
|
||||
eventImpl.eventPhase === Event.AT_TARGET
|
||||
) {
|
||||
invokeEventListeners(tuple, eventImpl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setEventPhase(eventImpl, Event.NONE);
|
||||
setCurrentTarget(eventImpl, null);
|
||||
setPath(eventImpl, []);
|
||||
setDispatched(eventImpl, false);
|
||||
eventImpl.cancelBubble = false;
|
||||
setStopImmediatePropagation(eventImpl, false);
|
||||
|
||||
if (clearTargets) {
|
||||
setTarget(eventImpl, null);
|
||||
setRelatedTarget(eventImpl, null);
|
||||
}
|
||||
|
||||
// TODO: invoke activation targets if HTML nodes will be implemented
|
||||
// if (activationTarget !== null) {
|
||||
// if (!eventImpl.defaultPrevented) {
|
||||
// activationTarget._activationBehavior();
|
||||
// }
|
||||
// }
|
||||
|
||||
return !eventImpl.defaultPrevented;
|
||||
}
|
||||
|
||||
/** Inner invoking of the event listeners where the resolved listeners are
|
||||
* called.
|
||||
*
|
||||
* Ref: https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke */
|
||||
function innerInvokeEventListeners(
|
||||
eventImpl: Event,
|
||||
targetListeners: Record<string, Listener[]>,
|
||||
): boolean {
|
||||
let found = false;
|
||||
|
||||
const { type } = eventImpl;
|
||||
|
||||
if (!targetListeners || !targetListeners[type]) {
|
||||
return found;
|
||||
}
|
||||
|
||||
// Copy event listeners before iterating since the list can be modified during the iteration.
|
||||
const handlers = targetListeners[type].slice();
|
||||
|
||||
for (let i = 0; i < handlers.length; i++) {
|
||||
const listener = handlers[i];
|
||||
|
||||
let capture, once, passive;
|
||||
if (typeof listener.options === "boolean") {
|
||||
capture = listener.options;
|
||||
once = false;
|
||||
passive = false;
|
||||
} else {
|
||||
capture = listener.options.capture;
|
||||
once = listener.options.once;
|
||||
passive = listener.options.passive;
|
||||
}
|
||||
|
||||
// Check if the event listener has been removed since the listeners has been cloned.
|
||||
if (!targetListeners[type].includes(listener)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
found = true;
|
||||
|
||||
if (
|
||||
(eventImpl.eventPhase === Event.CAPTURING_PHASE && !capture) ||
|
||||
(eventImpl.eventPhase === Event.BUBBLING_PHASE && capture)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (once) {
|
||||
targetListeners[type].splice(targetListeners[type].indexOf(listener), 1);
|
||||
}
|
||||
|
||||
if (passive) {
|
||||
setInPassiveListener(eventImpl, true);
|
||||
}
|
||||
|
||||
if (typeof listener.callback === "object") {
|
||||
if (typeof listener.callback.handleEvent === "function") {
|
||||
listener.callback.handleEvent(eventImpl);
|
||||
}
|
||||
} else {
|
||||
listener.callback.call(eventImpl.currentTarget, eventImpl);
|
||||
}
|
||||
|
||||
setInPassiveListener(eventImpl, false);
|
||||
|
||||
if (getStopImmediatePropagation(eventImpl)) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/** Invokes the listeners on a given event path with the supplied event.
|
||||
*
|
||||
* Ref: https://dom.spec.whatwg.org/#concept-event-listener-invoke */
|
||||
function invokeEventListeners(tuple: EventPath, eventImpl: Event): void {
|
||||
const path = getPath(eventImpl);
|
||||
const tupleIndex = path.indexOf(tuple);
|
||||
for (let i = tupleIndex; i >= 0; i--) {
|
||||
const t = path[i];
|
||||
if (t.target) {
|
||||
setTarget(eventImpl, t.target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setRelatedTarget(eventImpl, tuple.relatedTarget);
|
||||
|
||||
if (eventImpl.cancelBubble) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentTarget(eventImpl, tuple.item);
|
||||
|
||||
innerInvokeEventListeners(eventImpl, getListeners(tuple.item));
|
||||
}
|
||||
|
||||
function normalizeAddEventHandlerOptions(
|
||||
options: boolean | AddEventListenerOptions | undefined,
|
||||
): AddEventListenerOptions {
|
||||
if (typeof options === "boolean" || typeof options === "undefined") {
|
||||
return {
|
||||
capture: Boolean(options),
|
||||
once: false,
|
||||
passive: false,
|
||||
};
|
||||
} else {
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeEventHandlerOptions(
|
||||
options: boolean | EventListenerOptions | undefined,
|
||||
): EventListenerOptions {
|
||||
if (typeof options === "boolean" || typeof options === "undefined") {
|
||||
return {
|
||||
capture: Boolean(options),
|
||||
};
|
||||
} else {
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
/** Retarget the target following the spec logic.
|
||||
*
|
||||
* Ref: https://dom.spec.whatwg.org/#retarget */
|
||||
function retarget(a: EventTarget | null, b: EventTarget): EventTarget | null {
|
||||
while (true) {
|
||||
if (!isNode(a)) {
|
||||
return a;
|
||||
}
|
||||
|
||||
const aRoot = a.getRootNode();
|
||||
|
||||
if (aRoot) {
|
||||
if (
|
||||
!isShadowRoot(aRoot) ||
|
||||
(isNode(b) && isShadowInclusiveAncestor(aRoot, b))
|
||||
) {
|
||||
return a;
|
||||
}
|
||||
|
||||
a = getHost(aRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Non-public state information for an event target that needs to held onto.
|
||||
// Some of the information should be moved to other entities (like Node,
|
||||
// ShowRoot, UIElement, etc.).
|
||||
interface EventTargetData {
|
||||
assignedSlot: boolean;
|
||||
hasActivationBehavior: boolean;
|
||||
host: EventTarget | null;
|
||||
listeners: Record<string, Listener[]>;
|
||||
mode: string;
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
callback: EventListenerOrEventListenerObject;
|
||||
options: AddEventListenerOptions;
|
||||
}
|
||||
|
||||
// Accessors for non-public data
|
||||
|
||||
export const eventTargetData = new WeakMap<EventTarget, EventTargetData>();
|
||||
|
||||
function getAssignedSlot(target: EventTarget): boolean {
|
||||
return Boolean(eventTargetData.get(target as EventTarget)?.assignedSlot);
|
||||
}
|
||||
|
||||
function getHasActivationBehavior(target: EventTarget): boolean {
|
||||
return Boolean(
|
||||
eventTargetData.get(target as EventTarget)?.hasActivationBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
function getHost(target: EventTarget): EventTarget | null {
|
||||
return eventTargetData.get(target as EventTarget)?.host ?? null;
|
||||
}
|
||||
|
||||
function getListeners(target: EventTarget): Record<string, Listener[]> {
|
||||
return eventTargetData.get(target as EventTarget)?.listeners ?? {};
|
||||
}
|
||||
|
||||
function getMode(target: EventTarget): string | null {
|
||||
return eventTargetData.get(target as EventTarget)?.mode ?? null;
|
||||
}
|
||||
|
||||
export function getDefaultTargetData(): Readonly<EventTargetData> {
|
||||
return {
|
||||
assignedSlot: false,
|
||||
hasActivationBehavior: false,
|
||||
host: null,
|
||||
listeners: Object.create(null),
|
||||
mode: "",
|
||||
};
|
||||
}
|
||||
|
||||
export class EventTargetImpl implements EventTarget {
|
||||
constructor() {
|
||||
eventTargetData.set(this, getDefaultTargetData());
|
||||
}
|
||||
|
||||
public addEventListener(
|
||||
type: string,
|
||||
callback: EventListenerOrEventListenerObject | null,
|
||||
options?: AddEventListenerOptions | boolean,
|
||||
): void {
|
||||
requiredArguments("EventTarget.addEventListener", arguments.length, 2);
|
||||
if (callback === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
options = normalizeAddEventHandlerOptions(options);
|
||||
const { listeners } = eventTargetData.get(this ?? globalThis)!;
|
||||
|
||||
if (!(type in listeners)) {
|
||||
listeners[type] = [];
|
||||
}
|
||||
|
||||
for (const listener of listeners[type]) {
|
||||
if (
|
||||
((typeof listener.options === "boolean" &&
|
||||
listener.options === options.capture) ||
|
||||
(typeof listener.options === "object" &&
|
||||
listener.options.capture === options.capture)) &&
|
||||
listener.callback === callback
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
listeners[type].push({ callback, options });
|
||||
}
|
||||
|
||||
public removeEventListener(
|
||||
type: string,
|
||||
callback: EventListenerOrEventListenerObject | null,
|
||||
options?: EventListenerOptions | boolean,
|
||||
): void {
|
||||
requiredArguments("EventTarget.removeEventListener", arguments.length, 2);
|
||||
|
||||
const listeners = eventTargetData.get(this ?? globalThis)!.listeners;
|
||||
if (callback !== null && type in listeners) {
|
||||
listeners[type] = listeners[type].filter(
|
||||
(listener) => listener.callback !== callback,
|
||||
);
|
||||
} else if (callback === null || !listeners[type]) {
|
||||
return;
|
||||
}
|
||||
|
||||
options = normalizeEventHandlerOptions(options);
|
||||
|
||||
for (let i = 0; i < listeners[type].length; ++i) {
|
||||
const listener = listeners[type][i];
|
||||
if (
|
||||
((typeof listener.options === "boolean" &&
|
||||
listener.options === options.capture) ||
|
||||
(typeof listener.options === "object" &&
|
||||
listener.options.capture === options.capture)) &&
|
||||
listener.callback === callback
|
||||
) {
|
||||
listeners[type].splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public dispatchEvent(event: Event): boolean {
|
||||
requiredArguments("EventTarget.dispatchEvent", arguments.length, 1);
|
||||
const self = this ?? globalThis;
|
||||
|
||||
const listeners = eventTargetData.get(self)!.listeners;
|
||||
if (!(event.type in listeners)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getDispatched(event)) {
|
||||
throw new DOMException("Invalid event state.", "InvalidStateError");
|
||||
}
|
||||
|
||||
if (event.eventPhase !== Event.NONE) {
|
||||
throw new DOMException("Invalid event state.", "InvalidStateError");
|
||||
}
|
||||
|
||||
return dispatch(self, event);
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag](): string {
|
||||
return "EventTarget";
|
||||
}
|
||||
|
||||
protected getParent(_event: Event): EventTarget | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
defineEnumerableProps(EventTargetImpl, [
|
||||
"addEventListener",
|
||||
"removeEventListener",
|
||||
"dispatchEvent",
|
||||
]);
|
|
@ -1,361 +0,0 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { notImplemented } from "../util.ts";
|
||||
import { isTypedArray } from "./util.ts";
|
||||
import type * as domTypes from "./dom_types.d.ts";
|
||||
import { TextEncoder } from "./text_encoding.ts";
|
||||
import { DenoBlob, bytesSymbol as blobBytesSymbol } from "./blob.ts";
|
||||
import { read } from "../ops/io.ts";
|
||||
import { close } from "../ops/resources.ts";
|
||||
import { fetch as opFetch } from "../ops/fetch.ts";
|
||||
import type { FetchResponse } from "../ops/fetch.ts";
|
||||
import * as Body from "./body.ts";
|
||||
import { getHeaderValueParams } from "./util.ts";
|
||||
import { ReadableStreamImpl } from "./streams/readable_stream.ts";
|
||||
import { MultipartBuilder } from "./fetch/multipart.ts";
|
||||
|
||||
const NULL_BODY_STATUS = [101, 204, 205, 304];
|
||||
const REDIRECT_STATUS = [301, 302, 303, 307, 308];
|
||||
|
||||
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;
|
||||
|
||||
constructor(body: BodyInit | null = null, init?: domTypes.ResponseInit) {
|
||||
init = init ?? {};
|
||||
|
||||
if (typeof init !== "object") {
|
||||
throw new TypeError(`'init' is not an object`);
|
||||
}
|
||||
|
||||
const extraInit = responseData.get(init) || {};
|
||||
let { type = "default", url = "" } = extraInit;
|
||||
|
||||
let status = init.status === undefined ? 200 : Number(init.status || 0);
|
||||
let statusText = init.statusText ?? "";
|
||||
let headers = init.headers instanceof Headers
|
||||
? init.headers
|
||||
: new Headers(init.headers);
|
||||
|
||||
if (init.status !== undefined && (status < 200 || status > 599)) {
|
||||
throw new RangeError(
|
||||
`The status provided (${init.status}) is outside the range [200, 599]`,
|
||||
);
|
||||
}
|
||||
|
||||
// null body status
|
||||
if (body && NULL_BODY_STATUS.includes(status)) {
|
||||
throw new TypeError("Response with null body status cannot have body");
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
type = "default";
|
||||
} else {
|
||||
if (type == "error") {
|
||||
// spec: https://fetch.spec.whatwg.org/#concept-network-error
|
||||
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 headers) {
|
||||
/* Forbidden Response-Header Names:
|
||||
https://fetch.spec.whatwg.org/#forbidden-response-header-name */
|
||||
if (["set-cookie", "set-cookie2"].includes(h[0].toLowerCase())) {
|
||||
headers.delete(h[0]);
|
||||
}
|
||||
}
|
||||
} else if (type == "cors") {
|
||||
/* CORS-safelisted Response-Header Names:
|
||||
https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name */
|
||||
const allowedHeaders = [
|
||||
"Cache-Control",
|
||||
"Content-Language",
|
||||
"Content-Length",
|
||||
"Content-Type",
|
||||
"Expires",
|
||||
"Last-Modified",
|
||||
"Pragma",
|
||||
].map((c: string) => c.toLowerCase());
|
||||
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'
|
||||
However, this implementation of response doesn't seem to have an
|
||||
easy way to access the internal response, so we ignore that
|
||||
header.
|
||||
TODO(serverhiccups): change how internal responses are handled
|
||||
so we can do this properly. */
|
||||
if (!allowedHeaders.includes(h[0].toLowerCase())) {
|
||||
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") {
|
||||
url = "";
|
||||
status = 0;
|
||||
statusText = "";
|
||||
headers = new Headers();
|
||||
body = null;
|
||||
}
|
||||
}
|
||||
|
||||
const contentType = headers.get("content-type") || "";
|
||||
const size = Number(headers.get("content-length")) || undefined;
|
||||
|
||||
super(body, { contentType, size });
|
||||
|
||||
this.url = url;
|
||||
this.statusText = statusText;
|
||||
this.status = extraInit.status || status;
|
||||
this.headers = headers;
|
||||
this.redirected = extraInit.redirected || false;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
get ok(): boolean {
|
||||
return 200 <= this.status && this.status < 300;
|
||||
}
|
||||
|
||||
public clone(): domTypes.Response {
|
||||
if (this.bodyUsed) {
|
||||
throw TypeError(Body.BodyUsedError);
|
||||
}
|
||||
|
||||
const iterators = this.headers.entries();
|
||||
const headersList: Array<[string, string]> = [];
|
||||
for (const header of iterators) {
|
||||
headersList.push(header);
|
||||
}
|
||||
|
||||
let resBody = this._bodySource;
|
||||
|
||||
if (this._bodySource instanceof ReadableStreamImpl) {
|
||||
const tees = this._bodySource.tee();
|
||||
this._stream = this._bodySource = tees[0];
|
||||
resBody = tees[1];
|
||||
}
|
||||
|
||||
return new Response(resBody, {
|
||||
status: this.status,
|
||||
statusText: this.statusText,
|
||||
headers: new Headers(headersList),
|
||||
});
|
||||
}
|
||||
|
||||
static redirect(url: URL | string, status: number): domTypes.Response {
|
||||
if (![301, 302, 303, 307, 308].includes(status)) {
|
||||
throw new RangeError(
|
||||
"The redirection status must be one of 301, 302, 303, 307 and 308.",
|
||||
);
|
||||
}
|
||||
return new Response(null, {
|
||||
status,
|
||||
statusText: "",
|
||||
headers: [["Location", typeof url === "string" ? url : url.toString()]],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function sendFetchReq(
|
||||
url: string,
|
||||
method: string | null,
|
||||
headers: Headers | null,
|
||||
body: ArrayBufferView | undefined,
|
||||
): Promise<FetchResponse> {
|
||||
let headerArray: Array<[string, string]> = [];
|
||||
if (headers) {
|
||||
headerArray = Array.from(headers.entries());
|
||||
}
|
||||
|
||||
const args = {
|
||||
method,
|
||||
url,
|
||||
headers: headerArray,
|
||||
};
|
||||
|
||||
return opFetch(args, body);
|
||||
}
|
||||
|
||||
export async function fetch(
|
||||
input: (domTypes.Request & { _bodySource?: unknown }) | URL | string,
|
||||
init?: domTypes.RequestInit,
|
||||
): Promise<Response> {
|
||||
let url: string;
|
||||
let method: string | null = null;
|
||||
let headers: Headers | null = null;
|
||||
let body: ArrayBufferView | undefined;
|
||||
let redirected = false;
|
||||
let remRedirectCount = 20; // TODO: use a better way to handle
|
||||
|
||||
if (typeof input === "string" || input instanceof URL) {
|
||||
url = typeof input === "string" ? (input as string) : (input as URL).href;
|
||||
if (init != null) {
|
||||
method = init.method || null;
|
||||
if (init.headers) {
|
||||
headers = init.headers instanceof Headers
|
||||
? init.headers
|
||||
: new Headers(init.headers);
|
||||
} else {
|
||||
headers = null;
|
||||
}
|
||||
|
||||
// ref: https://fetch.spec.whatwg.org/#body-mixin
|
||||
// Body should have been a mixin
|
||||
// but we are treating it as a separate class
|
||||
if (init.body) {
|
||||
if (!headers) {
|
||||
headers = new Headers();
|
||||
}
|
||||
let contentType = "";
|
||||
if (typeof init.body === "string") {
|
||||
body = new TextEncoder().encode(init.body);
|
||||
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";
|
||||
} else if (init.body instanceof DenoBlob) {
|
||||
body = init.body[blobBytesSymbol];
|
||||
contentType = init.body.type;
|
||||
} else if (init.body instanceof FormData) {
|
||||
let boundary;
|
||||
if (headers.has("content-type")) {
|
||||
const params = getHeaderValueParams("content-type");
|
||||
boundary = params.get("boundary")!;
|
||||
}
|
||||
const multipartBuilder = new MultipartBuilder(init.body, boundary);
|
||||
body = multipartBuilder.getBody();
|
||||
contentType = multipartBuilder.getContentType();
|
||||
} else {
|
||||
// TODO: ReadableStream
|
||||
notImplemented();
|
||||
}
|
||||
if (contentType && !headers.has("content-type")) {
|
||||
headers.set("content-type", contentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
url = input.url;
|
||||
method = input.method;
|
||||
headers = input.headers;
|
||||
|
||||
if (input._bodySource) {
|
||||
body = new DataView(await input.arrayBuffer());
|
||||
}
|
||||
}
|
||||
|
||||
let responseBody;
|
||||
let responseInit: domTypes.ResponseInit = {};
|
||||
while (remRedirectCount) {
|
||||
const fetchResponse = await sendFetchReq(url, method, headers, body);
|
||||
|
||||
if (
|
||||
NULL_BODY_STATUS.includes(fetchResponse.status) ||
|
||||
REDIRECT_STATUS.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);
|
||||
responseBody = null;
|
||||
} else {
|
||||
responseBody = new ReadableStreamImpl({
|
||||
async pull(controller: ReadableStreamDefaultController): Promise<void> {
|
||||
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);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
responseInit = {
|
||||
status: 200,
|
||||
statusText: fetchResponse.statusText,
|
||||
headers: fetchResponse.headers,
|
||||
};
|
||||
|
||||
responseData.set(responseInit, {
|
||||
redirected,
|
||||
rid: fetchResponse.bodyRid,
|
||||
status: fetchResponse.status,
|
||||
url,
|
||||
});
|
||||
|
||||
const response = new Response(responseBody, responseInit);
|
||||
|
||||
if (REDIRECT_STATUS.includes(fetchResponse.status)) {
|
||||
// We're in a redirect status
|
||||
switch ((init && init.redirect) || "follow") {
|
||||
case "error":
|
||||
responseInit = {};
|
||||
responseData.set(responseInit, {
|
||||
type: "error",
|
||||
redirected: false,
|
||||
url: "",
|
||||
});
|
||||
return new Response(null, responseInit);
|
||||
case "manual":
|
||||
responseInit = {};
|
||||
responseData.set(responseInit, {
|
||||
type: "opaqueredirect",
|
||||
redirected: false,
|
||||
url: "",
|
||||
});
|
||||
return new Response(null, responseInit);
|
||||
case "follow":
|
||||
default:
|
||||
let redirectUrl = response.headers.get("Location");
|
||||
if (redirectUrl == null) {
|
||||
return response; // Unspecified
|
||||
}
|
||||
if (
|
||||
!redirectUrl.startsWith("http://") &&
|
||||
!redirectUrl.startsWith("https://")
|
||||
) {
|
||||
redirectUrl = new URL(redirectUrl, url).href;
|
||||
}
|
||||
url = redirectUrl;
|
||||
redirected = true;
|
||||
remRedirectCount--;
|
||||
}
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
responseData.set(responseInit, {
|
||||
type: "error",
|
||||
redirected: false,
|
||||
url: "",
|
||||
});
|
||||
|
||||
return new Response(null, responseInit);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue