0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-03 09:31:22 -05:00

fix(std/node): fix readFile types, add encoding types (#6451)

This commit is contained in:
Benjamin Lupton 2020-06-24 12:32:43 +10:00 committed by GitHub
parent d16337cc9c
commit 49c54c0805
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 159 additions and 50 deletions

View file

@ -1,5 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import {
Encodings,
WriteFileOptions,
isFileOptions,
CallbackWithError,
@ -15,13 +16,13 @@ import { fromFileUrl } from "../path.ts";
export function appendFile(
pathOrRid: string | number | URL,
data: string,
optionsOrCallback: string | WriteFileOptions | CallbackWithError,
optionsOrCallback: Encodings | WriteFileOptions | CallbackWithError,
callback?: CallbackWithError
): void {
pathOrRid = pathOrRid instanceof URL ? fromFileUrl(pathOrRid) : pathOrRid;
const callbackFn: CallbackWithError | undefined =
optionsOrCallback instanceof Function ? optionsOrCallback : callback;
const options: string | WriteFileOptions | undefined =
const options: Encodings | WriteFileOptions | undefined =
optionsOrCallback instanceof Function ? undefined : optionsOrCallback;
if (!callbackFn) {
throw new Error("No callback function supplied");
@ -79,7 +80,7 @@ function closeRidIfNecessary(isPathString: boolean, rid: number): void {
export function appendFileSync(
pathOrRid: string | number | URL,
data: string,
options?: string | WriteFileOptions
options?: Encodings | WriteFileOptions
): void {
let rid = -1;
@ -115,7 +116,7 @@ export function appendFileSync(
}
function validateEncoding(
encodingOption: string | WriteFileOptions | undefined
encodingOption: Encodings | WriteFileOptions | undefined
): void {
if (!encodingOption) return;

View file

@ -23,6 +23,7 @@ Deno.test({
fn() {
assertThrows(
() => {
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
appendFile("some/path", "some data", "made-up-encoding", () => {});
},
Error,
@ -33,6 +34,7 @@ Deno.test({
appendFile(
"some/path",
"some data",
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
{ encoding: "made-up-encoding" },
() => {}
);
@ -41,6 +43,7 @@ Deno.test({
"Only 'utf8' encoding is currently supported"
);
assertThrows(
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
() => appendFileSync("some/path", "some data", "made-up-encoding"),
Error,
"Only 'utf8' encoding is currently supported"
@ -48,6 +51,7 @@ Deno.test({
assertThrows(
() =>
appendFileSync("some/path", "some data", {
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
encoding: "made-up-encoding",
}),
Error,

View file

@ -4,11 +4,32 @@ import { notImplemented } from "../_utils.ts";
export type CallbackWithError = (err?: Error | null) => void;
export type TextEncodings =
| "ascii"
| "utf8"
| "utf-8"
| "utf16le"
| "ucs2"
| "ucs-2"
| "base64"
| "latin1"
| "hex";
export type BinaryEncodings = "binary";
export type Encodings = TextEncodings | BinaryEncodings;
export interface FileOptions {
encoding?: string;
encoding?: Encodings;
flag?: string;
}
export type TextOptionsArgument =
| TextEncodings
| ({ encoding: TextEncodings } & FileOptions);
export type BinaryOptionsArgument =
| BinaryEncodings
| ({ encoding: BinaryEncodings } & FileOptions);
export type FileOptionsArgument = Encodings | FileOptions;
export interface WriteFileOptions extends FileOptions {
mode?: number;
}
@ -26,8 +47,8 @@ export function isFileOptions(
}
export function getEncoding(
optOrCallback?: FileOptions | WriteFileOptions | Function | string
): string | null {
optOrCallback?: FileOptions | WriteFileOptions | Function | Encodings | null
): Encodings | null {
if (!optOrCallback || typeof optOrCallback === "function") {
return null;
}
@ -38,13 +59,15 @@ export function getEncoding(
return encoding;
}
export function checkEncoding(encoding: string | null): string | null {
export function checkEncoding(encoding: Encodings | null): Encodings | null {
if (!encoding) return null;
if (encoding === "utf8" || encoding === "utf-8") {
return "utf8";
}
if (encoding === "buffer") {
return "buffer";
if (encoding === "binary") {
return "binary";
// before this was buffer, however buffer is not used in Node
// node -e "require('fs').readFile('../world.txt', 'buffer', console.log)"
}
const notImplementedEncodings = [
@ -53,7 +76,6 @@ export function checkEncoding(encoding: string | null): string | null {
"base64",
"hex",
"ascii",
"binary",
"ucs2",
];

View file

@ -1,30 +1,58 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { intoCallbackAPIWithIntercept, MaybeEmpty } from "../_utils.ts";
import { getEncoding, FileOptions } from "./_fs_common.ts";
import {
Encodings,
getEncoding,
FileOptionsArgument,
TextOptionsArgument,
BinaryOptionsArgument,
TextEncodings,
BinaryEncodings,
} from "./_fs_common.ts";
import { Buffer } from "../buffer.ts";
import { fromFileUrl } from "../path.ts";
type ReadFileCallback = (
err: MaybeEmpty<Error>,
data: MaybeEmpty<string | Buffer>
) => void;
function maybeDecode(data: Uint8Array, encoding: TextEncodings): string;
function maybeDecode(
data: Uint8Array,
encoding: string | null
encoding: BinaryEncodings | null
): Buffer;
function maybeDecode(
data: Uint8Array,
encoding: Encodings | null
): string | Buffer {
const buffer = new Buffer(data.buffer, data.byteOffset, data.byteLength);
if (encoding) return buffer.toString(encoding);
if (encoding && encoding !== "binary") return buffer.toString(encoding);
return buffer;
}
type TextCallback = (err: Error | null, data?: string) => void;
type BinaryCallback = (err: Error | null, data?: Buffer) => void;
type GenericCallback = (err: Error | null, data?: string | Buffer) => void;
type Callback = TextCallback | BinaryCallback | GenericCallback;
export function readFile(
path: string | URL,
optOrCallback: ReadFileCallback | FileOptions | string | undefined,
callback?: ReadFileCallback
options: TextOptionsArgument,
callback: TextCallback
): void;
export function readFile(
path: string | URL,
options: BinaryOptionsArgument,
callback: BinaryCallback
): void;
export function readFile(
path: string | URL,
options: null | undefined | FileOptionsArgument,
callback: BinaryCallback
): void;
export function readFile(path: string | URL, callback: BinaryCallback): void;
export function readFile(
path: string | URL,
optOrCallback?: FileOptionsArgument | Callback | null | undefined,
callback?: Callback
): void {
path = path instanceof URL ? fromFileUrl(path) : path;
let cb: ReadFileCallback | undefined;
let cb: Callback | undefined;
if (typeof optOrCallback === "function") {
cb = optOrCallback;
} else {
@ -33,18 +61,39 @@ export function readFile(
const encoding = getEncoding(optOrCallback);
intoCallbackAPIWithIntercept<Uint8Array, string | Buffer>(
Deno.readFile,
(data: Uint8Array): string | Buffer => maybeDecode(data, encoding),
cb,
path
);
const p = Deno.readFile(path);
if (cb) {
p.then((data: Uint8Array) => {
if (encoding && encoding !== "binary") {
const text = maybeDecode(data, encoding);
return (cb as TextCallback)(null, text);
}
const buffer = maybeDecode(data, encoding);
(cb as BinaryCallback)(null, buffer);
}).catch((err) => cb && cb(err));
}
}
export function readFileSync(
path: string | URL,
opt?: FileOptions | string
opt: TextOptionsArgument
): string;
export function readFileSync(
path: string | URL,
opt?: BinaryOptionsArgument
): Buffer;
export function readFileSync(
path: string | URL,
opt?: FileOptionsArgument
): string | Buffer {
path = path instanceof URL ? fromFileUrl(path) : path;
return maybeDecode(Deno.readFileSync(path), getEncoding(opt));
const data = Deno.readFileSync(path);
const encoding = getEncoding(opt);
if (encoding && encoding !== "binary") {
const text = maybeDecode(data, encoding);
return text;
}
const buffer = maybeDecode(data, encoding);
return buffer;
}

View file

@ -3,6 +3,7 @@ import { notImplemented } from "../_utils.ts";
import { fromFileUrl } from "../path.ts";
import {
Encodings,
WriteFileOptions,
CallbackWithError,
isFileOptions,
@ -14,12 +15,12 @@ import {
export function writeFile(
pathOrRid: string | number | URL,
data: string | Uint8Array,
optOrCallback: string | CallbackWithError | WriteFileOptions | undefined,
optOrCallback: Encodings | CallbackWithError | WriteFileOptions | undefined,
callback?: CallbackWithError
): void {
const callbackFn: CallbackWithError | undefined =
optOrCallback instanceof Function ? optOrCallback : callback;
const options: string | WriteFileOptions | undefined =
const options: Encodings | WriteFileOptions | undefined =
optOrCallback instanceof Function ? undefined : optOrCallback;
if (!callbackFn) {
@ -71,7 +72,7 @@ export function writeFile(
export function writeFileSync(
pathOrRid: string | number | URL,
data: string | Uint8Array,
options?: string | WriteFileOptions
options?: Encodings | WriteFileOptions
): void {
pathOrRid = pathOrRid instanceof URL ? fromFileUrl(pathOrRid) : pathOrRid;

View file

@ -24,6 +24,7 @@ Deno.test("Callback must be a function error", function fn() {
Deno.test("Invalid encoding results in error()", function testEncodingErrors() {
assertThrows(
() => {
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
writeFile("some/path", "some data", "made-up-encoding", () => {});
},
Error,
@ -32,6 +33,7 @@ Deno.test("Invalid encoding results in error()", function testEncodingErrors() {
assertThrows(
() => {
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
writeFileSync("some/path", "some data", "made-up-encoding");
},
Error,
@ -44,6 +46,7 @@ Deno.test("Invalid encoding results in error()", function testEncodingErrors() {
"some/path",
"some data",
{
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
encoding: "made-up-encoding",
},
() => {}
@ -56,6 +59,7 @@ Deno.test("Invalid encoding results in error()", function testEncodingErrors() {
assertThrows(
() => {
writeFileSync("some/path", "some data", {
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
encoding: "made-up-encoding",
});
},

View file

@ -1,16 +1,28 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { FileOptions } from "../_fs_common.ts";
import { MaybeEmpty } from "../../_utils.ts";
import {
FileOptionsArgument,
BinaryOptionsArgument,
TextOptionsArgument,
} from "../_fs_common.ts";
import { readFile as readFileCallback } from "../_fs_readFile.ts";
export function readFile(
path: string | URL,
options?: FileOptions | string
): Promise<MaybeEmpty<string | Uint8Array>> {
options: TextOptionsArgument
): Promise<string>;
export function readFile(
path: string | URL,
options?: BinaryOptionsArgument
): Promise<Uint8Array>;
export function readFile(
path: string | URL,
options?: FileOptionsArgument
): Promise<string | Uint8Array> {
return new Promise((resolve, reject) => {
readFileCallback(path, options, (err, data): void => {
if (err) return reject(err);
if (data == null)
return reject(new Error("Invalid state: data missing, but no error"));
resolve(data);
});
});

View file

@ -7,24 +7,38 @@ const testData = path.resolve(
);
Deno.test("readFileSuccess", async function () {
const data = await readFile(testData);
const data: Uint8Array = await readFile(testData);
assert(data instanceof Uint8Array);
assertEquals(new TextDecoder().decode(data as Uint8Array), "hello world");
assertEquals(new TextDecoder().decode(data), "hello world");
});
Deno.test("readFileEncodeUtf8Success", async function () {
const data = await readFile(testData, { encoding: "utf8" });
Deno.test("readFileBinarySuccess", async function () {
const data: Uint8Array = await readFile(testData, "binary");
assertEquals(typeof data, "string");
assertEquals(data as string, "hello world");
assert(data instanceof Uint8Array);
assertEquals(new TextDecoder().decode(data), "hello world");
});
Deno.test("readFileEncodingAsString", async function () {
const data = await readFile(testData, "utf8");
Deno.test("readFileBinaryObjectSuccess", async function () {
const data: Uint8Array = await readFile(testData, { encoding: "binary" });
assert(data instanceof Uint8Array);
assertEquals(new TextDecoder().decode(data), "hello world");
});
Deno.test("readFileStringObjectSuccess", async function () {
const data: string = await readFile(testData, { encoding: "utf8" });
assertEquals(typeof data, "string");
assertEquals(data as string, "hello world");
assertEquals(data, "hello world");
});
Deno.test("readFileStringSuccess", async function () {
const data: string = await readFile(testData, "utf8");
assertEquals(typeof data, "string");
assertEquals(data, "hello world");
});
Deno.test("readFileError", async function () {

View file

@ -1,12 +1,12 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { WriteFileOptions } from "../_fs_common.ts";
import { Encodings, WriteFileOptions } from "../_fs_common.ts";
import { writeFile as writeFileCallback } from "../_fs_writeFile.ts";
export function writeFile(
pathOrRid: string | number | URL,
data: string | Uint8Array,
options?: string | WriteFileOptions
options?: Encodings | WriteFileOptions
): Promise<void> {
return new Promise((resolve, reject) => {
writeFileCallback(pathOrRid, data, options, (err?: Error | null) => {

View file

@ -12,6 +12,7 @@ const decoder = new TextDecoder("utf-8");
Deno.test("Invalid encoding results in error()", function testEncodingErrors() {
assertThrowsAsync(
async () => {
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
await writeFile("some/path", "some data", "made-up-encoding");
},
Error,
@ -20,6 +21,7 @@ Deno.test("Invalid encoding results in error()", function testEncodingErrors() {
assertThrowsAsync(
async () => {
await writeFile("some/path", "some data", {
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
encoding: "made-up-encoding",
});
},