diff --git a/std/node/_fs/_fs_appendFile.ts b/std/node/_fs/_fs_appendFile.ts index 193badf1f0..49a4fc29f0 100644 --- a/std/node/_fs/_fs_appendFile.ts +++ b/std/node/_fs/_fs_appendFile.ts @@ -3,8 +3,8 @@ import { FileOptions, isFileOptions, CallbackWithError } from "./_fs_common.ts"; import { notImplemented } from "../_utils.ts"; /** - * TODO: Also accept 'data' parameter as a Node polyfill Buffer type once this - * is implemented. See https://github.com/denoland/deno/issues/3403 + * TODO: Also accept 'data' parameter as a Node polyfill Buffer or URL type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 */ export function appendFile( pathOrRid: string | number, @@ -69,8 +69,8 @@ function closeRidIfNecessary(isPathString: boolean, rid: number): void { } /** - * TODO: Also accept 'data' parameter as a Node polyfill Buffer type once this - * is implemented. See https://github.com/denoland/deno/issues/3403 + * TODO: Also accept 'data' parameter as a Node polyfill Buffer or URL type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 */ export function appendFileSync( pathOrRid: string | number, diff --git a/std/node/_fs/_fs_chmod.ts b/std/node/_fs/_fs_chmod.ts new file mode 100644 index 0000000000..cecb878eca --- /dev/null +++ b/std/node/_fs/_fs_chmod.ts @@ -0,0 +1,52 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +import { CallbackWithError } from "./_fs_common.ts"; + +const allowedModes = /^[0-7]{3}/; + +/** + * TODO: Also accept 'path' parameter as a Node polyfill Buffer or URL type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 + */ +export function chmod( + path: string, + mode: string | number, + callback: CallbackWithError +): void { + new Promise(async (resolve, reject) => { + try { + await Deno.chmod(path, getResolvedMode(mode)); + resolve(); + } catch (err) { + reject(err); + } + }) + .then(() => { + callback(); + }) + .catch(err => { + callback(err); + }); +} + +/** + * TODO: Also accept 'path' parameter as a Node polyfill Buffer or URL type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 + */ +export function chmodSync(path: string, mode: string | number): void { + Deno.chmodSync(path, getResolvedMode(mode)); +} + +function getResolvedMode(mode: string | number): number { + if (typeof mode === "number") { + return mode; + } + + if (typeof mode === "string") { + if (!allowedModes.test(mode)) { + throw new Error("Unrecognized mode: " + mode); + } + } + + return parseInt(mode, 8); +} diff --git a/std/node/_fs/_fs_chmod_test.ts b/std/node/_fs/_fs_chmod_test.ts new file mode 100644 index 0000000000..9be6669f29 --- /dev/null +++ b/std/node/_fs/_fs_chmod_test.ts @@ -0,0 +1,75 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +const { test } = Deno; +import { fail, assert } from "../../testing/asserts.ts"; +import { chmod, chmodSync } from "./_fs_chmod.ts"; + +if (Deno.build.os !== "win") { + test({ + name: "ASYNC: Permissions are changed (non-Windows)", + async fn() { + const tempFile: string = await Deno.makeTempFile(); + const originalFileMode: number | null = (await Deno.lstat(tempFile)).mode; + await new Promise((resolve, reject) => { + chmod(tempFile, 0o777, err => { + if (err) reject(err); + else resolve(); + }); + }) + .then(() => { + const newFileMode: number | null = Deno.lstatSync(tempFile).mode; + assert(newFileMode && originalFileMode); + assert(newFileMode === 33279 && newFileMode > originalFileMode); + }) + .catch(() => { + fail(); + }) + .finally(() => { + Deno.removeSync(tempFile); + }); + } + }); + + test({ + name: "SYNC: Permissions are changed (non-Windows)", + fn() { + const tempFile: string = Deno.makeTempFileSync(); + const originalFileMode: number | null = Deno.lstatSync(tempFile).mode; + chmodSync(tempFile, "777"); + + const newFileMode: number | null = Deno.lstatSync(tempFile).mode; + assert(newFileMode && originalFileMode); + assert(newFileMode === 33279 && newFileMode > originalFileMode); + Deno.removeSync(tempFile); + } + }); +} + +test({ + name: "ASYNC: Error passed in callback function when bad mode passed in", + async fn() { + await new Promise((resolve, reject) => { + chmod("some_pretend_file.txt", "999", err => { + if (err) reject(err); + else resolve(); + }); + }) + .then(() => { + fail("Expected exception to be thrown"); + }) + .catch(err => { + assert(err); + }); + } +}); +test({ + name: "SYNC: Error thrown when bad mode passed in", + fn() { + let caughtError: Error | undefined; + try { + chmodSync("some_pretend_file.txt", "999"); + } catch (err) { + caughtError = err; + } + assert(caughtError); + } +}); diff --git a/std/node/_fs/_fs_constants.ts b/std/node/_fs/_fs_constants.ts new file mode 100644 index 0000000000..765e4af790 --- /dev/null +++ b/std/node/_fs/_fs_constants.ts @@ -0,0 +1,18 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +//File access constants +export const F_OK = 0; +export const R_OK = 4; +export const W_OK = 2; +export const X_OK = 1; + +//File mode constants +export const S_IRUSR = 0o400; //read by owner +export const S_IWUSR = 0o200; //write by owner +export const S_IXUSR = 0o100; //execute/search by owner +export const S_IRGRP = 0o40; //read by group +export const S_IWGRP = 0o20; //write by group +export const S_IXGRP = 0o10; //execute/search by group +export const S_IROTH = 0o4; //read by others +export const S_IWOTH = 0o2; //write by others +export const S_IXOTH = 0o1; //execute/search by others diff --git a/std/node/fs.ts b/std/node/fs.ts index 6ef69cb694..f86fc2391c 100755 --- a/std/node/fs.ts +++ b/std/node/fs.ts @@ -8,6 +8,12 @@ import { import { appendFile, appendFileSync } from "./_fs/_fs_appendFile.ts"; export { appendFile, appendFileSync }; +import { chmod, chmodSync } from "./_fs/_fs_chmod.ts"; +export { chmod, chmodSync }; + +import * as constants from "./_fs/_fs_constants.ts"; +export { constants }; + const { readFile: denoReadFile, readFileSync: denoReadFileSync,