diff --git a/BUILD.gn b/BUILD.gn
index 7c29e42583..259c5bdc44 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -72,6 +72,7 @@ ts_sources = [
"js/console.ts",
"js/copy_file.ts",
"js/deno.ts",
+ "js/dir.ts",
"js/dispatch.ts",
"js/dom_types.ts",
"js/errors.ts",
@@ -107,7 +108,6 @@ ts_sources = [
"js/util.ts",
"js/v8_source_maps.ts",
"js/write_file.ts",
-
"tsconfig.json",
# Listing package.json and yarn.lock as sources ensures the bundle is rebuilt
diff --git a/js/deno.ts b/js/deno.ts
index 2125445411..565d4a7e6c 100644
--- a/js/deno.ts
+++ b/js/deno.ts
@@ -2,6 +2,7 @@
// Public deno module.
///
export { env, exit } from "./os";
+export { chdir, cwd } from "./dir";
export { File, open, stdin, stdout, stderr, read, write, close } from "./files";
export {
copy,
diff --git a/js/dir.ts b/js/dir.ts
new file mode 100644
index 0000000000..4bd3e6bd7c
--- /dev/null
+++ b/js/dir.ts
@@ -0,0 +1,37 @@
+// Copyright 2018 the Deno authors. All rights reserved. MIT license.
+import * as msg from "gen/msg_generated";
+import { assert } from "./util";
+import { flatbuffers } from "flatbuffers";
+import { sendSync } from "./dispatch";
+
+/**
+ * cwd() Return a string representing the current working directory.
+ * If the current directory can be reached via multiple paths
+ * (due to symbolic links), cwd() may return
+ * any one of them.
+ * throws NotFound exception if directory not available
+ */
+export function cwd(): string {
+ const builder = new flatbuffers.Builder(0);
+ msg.Cwd.startCwd(builder);
+ const inner = msg.Cwd.endCwd(builder);
+ const baseRes = sendSync(builder, msg.Any.Cwd, inner);
+ assert(baseRes != null);
+ assert(msg.Any.CwdRes === baseRes!.innerType());
+ const res = new msg.CwdRes();
+ assert(baseRes!.inner(res) != null);
+ return res.cwd()!;
+}
+
+/**
+ * chdir() Change the current working directory to path.
+ * throws NotFound exception if directory not available
+ */
+export function chdir(directory: string): void {
+ const builder = new flatbuffers.Builder();
+ const directory_ = builder.createString(directory);
+ msg.Chdir.startChdir(builder);
+ msg.Chdir.addDirectory(builder, directory_);
+ const inner = msg.Chdir.endChdir(builder);
+ sendSync(builder, msg.Any.Chdir, inner);
+}
diff --git a/js/dir_test.ts b/js/dir_test.ts
new file mode 100644
index 0000000000..ff55a0e696
--- /dev/null
+++ b/js/dir_test.ts
@@ -0,0 +1,54 @@
+import { test, testPerm, assert, assertEqual } from "./test_util.ts";
+import * as deno from "deno";
+
+test(function dirCwdNotNull() {
+ assert(deno.cwd() != null);
+});
+
+testPerm({ write: true }, function dirCwdChdirSuccess() {
+ const initialdir = deno.cwd();
+ const path = deno.makeTempDirSync();
+ deno.chdir(path);
+ const current = deno.cwd();
+ if (deno.platform.os === "mac") {
+ assertEqual(current, "/private" + path);
+ } else {
+ assertEqual(current, path);
+ }
+ deno.chdir(initialdir);
+});
+
+testPerm({ write: true }, function dirCwdError() {
+ // excluding windows since it throws resource busy, while removeSync
+ if (["linux", "mac"].includes(deno.platform.os)) {
+ const initialdir = deno.cwd();
+ const path = deno.makeTempDirSync();
+ deno.chdir(path);
+ deno.removeSync(path);
+ try {
+ deno.cwd();
+ throw Error("current directory removed, should throw error");
+ } catch (err) {
+ if (err instanceof deno.DenoError) {
+ console.log(err.name === "NotFound");
+ } else {
+ throw Error("raised different exception");
+ }
+ }
+ deno.chdir(initialdir);
+ }
+});
+
+testPerm({ write: true }, function dirChdirError() {
+ const path = deno.makeTempDirSync() + "test";
+ try {
+ deno.chdir(path);
+ throw Error("directory not available, should throw error");
+ } catch (err) {
+ if (err instanceof deno.DenoError) {
+ console.log(err.name === "NotFound");
+ } else {
+ throw Error("raised different exception");
+ }
+ }
+});
diff --git a/js/unit_tests.ts b/js/unit_tests.ts
index 24fdac8234..e09d0aba02 100644
--- a/js/unit_tests.ts
+++ b/js/unit_tests.ts
@@ -11,6 +11,7 @@ import "./read_dir_test.ts";
import "./write_file_test.ts";
import "./copy_file_test.ts";
import "./mkdir_test.ts";
+import "./dir_test";
import "./make_temp_dir_test.ts";
import "./stat_test.ts";
import "./rename_test.ts";
diff --git a/src/msg.fbs b/src/msg.fbs
index 43fde4d688..5b60213ade 100644
--- a/src/msg.fbs
+++ b/src/msg.fbs
@@ -41,6 +41,9 @@ union Any {
Accept,
Dial,
NewConn,
+ Chdir,
+ Cwd,
+ CwdRes,
Metrics,
MetricsRes,
}
@@ -93,6 +96,12 @@ enum ErrorKind: byte {
HttpOther,
}
+table Cwd {}
+
+table CwdRes {
+ cwd: string;
+}
+
table Base {
cmd_id: uint32;
sync: bool = true; // TODO(ry) Change default to false.
@@ -135,6 +144,10 @@ table CodeCache {
output_code: string;
}
+table Chdir {
+ directory: string;
+}
+
table SetTimeout {
timeout: double;
}
diff --git a/src/ops.rs b/src/ops.rs
index a58532bafc..261ed67f5d 100644
--- a/src/ops.rs
+++ b/src/ops.rs
@@ -101,6 +101,8 @@ pub fn dispatch(
msg::Any::Listen => op_listen,
msg::Any::Accept => op_accept,
msg::Any::Dial => op_dial,
+ msg::Any::Chdir => op_chdir,
+ msg::Any::Cwd => op_cwd,
msg::Any::Metrics => op_metrics,
_ => panic!(format!(
"Unhandled message {}",
@@ -110,8 +112,7 @@ pub fn dispatch(
op_creator(isolate.state.clone(), &base, data)
};
- let boxed_op = Box::new(
- op.or_else(move |err: DenoError| -> DenoResult {
+ let boxed_op = Box::new(op.or_else(move |err: DenoError| -> DenoResult {
debug!("op err {}", err);
// No matter whether we got an Err or Ok, we want a serialized message to
// send back. So transform the DenoError into a deno_buf.
@@ -143,8 +144,7 @@ pub fn dispatch(
)
};
Ok(buf)
- }),
- );
+ }));
debug!(
"msg_from_js {} sync {}",
@@ -283,6 +283,20 @@ fn op_code_cache(
}()))
}
+fn op_chdir(
+ _state: Arc,
+ base: &msg::Base,
+ data: &'static mut [u8],
+) -> Box {
+ assert_eq!(data.len(), 0);
+ let inner = base.inner_as_chdir().unwrap();
+ let directory = inner.directory().unwrap();
+ Box::new(futures::future::result(|| -> OpResult {
+ let _result = std::env::set_current_dir(&directory)?;
+ Ok(empty_buf())
+ }()))
+}
+
fn op_set_timeout(
isolate: &mut Isolate,
base: &msg::Base,
@@ -811,6 +825,37 @@ fn get_mode(_perm: fs::Permissions) -> u32 {
0
}
+fn op_cwd(
+ _state: Arc,
+ base: &msg::Base,
+ data: &'static mut [u8],
+) -> Box {
+ assert_eq!(data.len(), 0);
+ let cmd_id = base.cmd_id();
+ Box::new(futures::future::result(|| -> OpResult {
+ let path = std::env::current_dir()?;
+ let builder = &mut FlatBufferBuilder::new();
+ let cwd =
+ builder.create_string(&path.into_os_string().into_string().unwrap());
+ let inner = msg::CwdRes::create(
+ builder,
+ &msg::CwdResArgs {
+ cwd: Some(cwd),
+ ..Default::default()
+ },
+ );
+ Ok(serialize_response(
+ cmd_id,
+ builder,
+ msg::BaseArgs {
+ inner: Some(inner.as_union_value()),
+ inner_type: msg::Any::CwdRes,
+ ..Default::default()
+ },
+ ))
+ }()))
+}
+
fn op_stat(
_config: Arc,
base: &msg::Base,