Skip to content

Commit ed9021e

Browse files
authored
fix(std/node): fix readFile types, add encoding types (denoland/deno#6451)
1 parent ffc013e commit ed9021e

10 files changed

+158
-49
lines changed

node/_fs/_fs_appendFile.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
22
import {
3+
Encodings,
34
WriteFileOptions,
45
isFileOptions,
56
CallbackWithError,
@@ -15,13 +16,13 @@ import { fromFileUrl } from "../path.ts";
1516
export function appendFile(
1617
pathOrRid: string | number | URL,
1718
data: string,
18-
optionsOrCallback: string | WriteFileOptions | CallbackWithError,
19+
optionsOrCallback: Encodings | WriteFileOptions | CallbackWithError,
1920
callback?: CallbackWithError
2021
): void {
2122
pathOrRid = pathOrRid instanceof URL ? fromFileUrl(pathOrRid) : pathOrRid;
2223
const callbackFn: CallbackWithError | undefined =
2324
optionsOrCallback instanceof Function ? optionsOrCallback : callback;
24-
const options: string | WriteFileOptions | undefined =
25+
const options: Encodings | WriteFileOptions | undefined =
2526
optionsOrCallback instanceof Function ? undefined : optionsOrCallback;
2627
if (!callbackFn) {
2728
throw new Error("No callback function supplied");
@@ -79,7 +80,7 @@ function closeRidIfNecessary(isPathString: boolean, rid: number): void {
7980
export function appendFileSync(
8081
pathOrRid: string | number | URL,
8182
data: string,
82-
options?: string | WriteFileOptions
83+
options?: Encodings | WriteFileOptions
8384
): void {
8485
let rid = -1;
8586

@@ -115,7 +116,7 @@ export function appendFileSync(
115116
}
116117

117118
function validateEncoding(
118-
encodingOption: string | WriteFileOptions | undefined
119+
encodingOption: Encodings | WriteFileOptions | undefined
119120
): void {
120121
if (!encodingOption) return;
121122

node/_fs/_fs_appendFile_test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Deno.test({
2323
fn() {
2424
assertThrows(
2525
() => {
26+
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
2627
appendFile("some/path", "some data", "made-up-encoding", () => {});
2728
},
2829
Error,
@@ -33,6 +34,7 @@ Deno.test({
3334
appendFile(
3435
"some/path",
3536
"some data",
37+
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
3638
{ encoding: "made-up-encoding" },
3739
() => {}
3840
);
@@ -41,13 +43,15 @@ Deno.test({
4143
"Only 'utf8' encoding is currently supported"
4244
);
4345
assertThrows(
46+
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
4447
() => appendFileSync("some/path", "some data", "made-up-encoding"),
4548
Error,
4649
"Only 'utf8' encoding is currently supported"
4750
);
4851
assertThrows(
4952
() =>
5053
appendFileSync("some/path", "some data", {
54+
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
5155
encoding: "made-up-encoding",
5256
}),
5357
Error,

node/_fs/_fs_common.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,32 @@ import { notImplemented } from "../_utils.ts";
44

55
export type CallbackWithError = (err?: Error | null) => void;
66

7+
export type TextEncodings =
8+
| "ascii"
9+
| "utf8"
10+
| "utf-8"
11+
| "utf16le"
12+
| "ucs2"
13+
| "ucs-2"
14+
| "base64"
15+
| "latin1"
16+
| "hex";
17+
export type BinaryEncodings = "binary";
18+
export type Encodings = TextEncodings | BinaryEncodings;
19+
720
export interface FileOptions {
8-
encoding?: string;
21+
encoding?: Encodings;
922
flag?: string;
1023
}
1124

25+
export type TextOptionsArgument =
26+
| TextEncodings
27+
| ({ encoding: TextEncodings } & FileOptions);
28+
export type BinaryOptionsArgument =
29+
| BinaryEncodings
30+
| ({ encoding: BinaryEncodings } & FileOptions);
31+
export type FileOptionsArgument = Encodings | FileOptions;
32+
1233
export interface WriteFileOptions extends FileOptions {
1334
mode?: number;
1435
}
@@ -26,8 +47,8 @@ export function isFileOptions(
2647
}
2748

2849
export function getEncoding(
29-
optOrCallback?: FileOptions | WriteFileOptions | Function | string
30-
): string | null {
50+
optOrCallback?: FileOptions | WriteFileOptions | Function | Encodings | null
51+
): Encodings | null {
3152
if (!optOrCallback || typeof optOrCallback === "function") {
3253
return null;
3354
}
@@ -38,13 +59,15 @@ export function getEncoding(
3859
return encoding;
3960
}
4061

41-
export function checkEncoding(encoding: string | null): string | null {
62+
export function checkEncoding(encoding: Encodings | null): Encodings | null {
4263
if (!encoding) return null;
4364
if (encoding === "utf8" || encoding === "utf-8") {
4465
return "utf8";
4566
}
46-
if (encoding === "buffer") {
47-
return "buffer";
67+
if (encoding === "binary") {
68+
return "binary";
69+
// before this was buffer, however buffer is not used in Node
70+
// node -e "require('fs').readFile('../world.txt', 'buffer', console.log)"
4871
}
4972

5073
const notImplementedEncodings = [
@@ -53,7 +76,6 @@ export function checkEncoding(encoding: string | null): string | null {
5376
"base64",
5477
"hex",
5578
"ascii",
56-
"binary",
5779
"ucs2",
5880
];
5981

node/_fs/_fs_readFile.ts

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,58 @@
11
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
2-
import { intoCallbackAPIWithIntercept, MaybeEmpty } from "../_utils.ts";
3-
import { getEncoding, FileOptions } from "./_fs_common.ts";
2+
import {
3+
Encodings,
4+
getEncoding,
5+
FileOptionsArgument,
6+
TextOptionsArgument,
7+
BinaryOptionsArgument,
8+
TextEncodings,
9+
BinaryEncodings,
10+
} from "./_fs_common.ts";
411
import { Buffer } from "../buffer.ts";
512
import { fromFileUrl } from "../path.ts";
613

7-
type ReadFileCallback = (
8-
err: MaybeEmpty<Error>,
9-
data: MaybeEmpty<string | Buffer>
10-
) => void;
11-
14+
function maybeDecode(data: Uint8Array, encoding: TextEncodings): string;
15+
function maybeDecode(
16+
data: Uint8Array,
17+
encoding: BinaryEncodings | null
18+
): Buffer;
1219
function maybeDecode(
1320
data: Uint8Array,
14-
encoding: string | null
21+
encoding: Encodings | null
1522
): string | Buffer {
1623
const buffer = new Buffer(data.buffer, data.byteOffset, data.byteLength);
17-
if (encoding) return buffer.toString(encoding);
24+
if (encoding && encoding !== "binary") return buffer.toString(encoding);
1825
return buffer;
1926
}
2027

28+
type TextCallback = (err: Error | null, data?: string) => void;
29+
type BinaryCallback = (err: Error | null, data?: Buffer) => void;
30+
type GenericCallback = (err: Error | null, data?: string | Buffer) => void;
31+
type Callback = TextCallback | BinaryCallback | GenericCallback;
32+
33+
export function readFile(
34+
path: string | URL,
35+
options: TextOptionsArgument,
36+
callback: TextCallback
37+
): void;
38+
export function readFile(
39+
path: string | URL,
40+
options: BinaryOptionsArgument,
41+
callback: BinaryCallback
42+
): void;
43+
export function readFile(
44+
path: string | URL,
45+
options: null | undefined | FileOptionsArgument,
46+
callback: BinaryCallback
47+
): void;
48+
export function readFile(path: string | URL, callback: BinaryCallback): void;
2149
export function readFile(
2250
path: string | URL,
23-
optOrCallback: ReadFileCallback | FileOptions | string | undefined,
24-
callback?: ReadFileCallback
51+
optOrCallback?: FileOptionsArgument | Callback | null | undefined,
52+
callback?: Callback
2553
): void {
2654
path = path instanceof URL ? fromFileUrl(path) : path;
27-
let cb: ReadFileCallback | undefined;
55+
let cb: Callback | undefined;
2856
if (typeof optOrCallback === "function") {
2957
cb = optOrCallback;
3058
} else {
@@ -33,18 +61,39 @@ export function readFile(
3361

3462
const encoding = getEncoding(optOrCallback);
3563

36-
intoCallbackAPIWithIntercept<Uint8Array, string | Buffer>(
37-
Deno.readFile,
38-
(data: Uint8Array): string | Buffer => maybeDecode(data, encoding),
39-
cb,
40-
path
41-
);
64+
const p = Deno.readFile(path);
65+
66+
if (cb) {
67+
p.then((data: Uint8Array) => {
68+
if (encoding && encoding !== "binary") {
69+
const text = maybeDecode(data, encoding);
70+
return (cb as TextCallback)(null, text);
71+
}
72+
const buffer = maybeDecode(data, encoding);
73+
(cb as BinaryCallback)(null, buffer);
74+
}).catch((err) => cb && cb(err));
75+
}
4276
}
4377

4478
export function readFileSync(
4579
path: string | URL,
46-
opt?: FileOptions | string
80+
opt: TextOptionsArgument
81+
): string;
82+
export function readFileSync(
83+
path: string | URL,
84+
opt?: BinaryOptionsArgument
85+
): Buffer;
86+
export function readFileSync(
87+
path: string | URL,
88+
opt?: FileOptionsArgument
4789
): string | Buffer {
4890
path = path instanceof URL ? fromFileUrl(path) : path;
49-
return maybeDecode(Deno.readFileSync(path), getEncoding(opt));
91+
const data = Deno.readFileSync(path);
92+
const encoding = getEncoding(opt);
93+
if (encoding && encoding !== "binary") {
94+
const text = maybeDecode(data, encoding);
95+
return text;
96+
}
97+
const buffer = maybeDecode(data, encoding);
98+
return buffer;
5099
}

node/_fs/_fs_writeFile.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { notImplemented } from "../_utils.ts";
33
import { fromFileUrl } from "../path.ts";
44

55
import {
6+
Encodings,
67
WriteFileOptions,
78
CallbackWithError,
89
isFileOptions,
@@ -14,12 +15,12 @@ import {
1415
export function writeFile(
1516
pathOrRid: string | number | URL,
1617
data: string | Uint8Array,
17-
optOrCallback: string | CallbackWithError | WriteFileOptions | undefined,
18+
optOrCallback: Encodings | CallbackWithError | WriteFileOptions | undefined,
1819
callback?: CallbackWithError
1920
): void {
2021
const callbackFn: CallbackWithError | undefined =
2122
optOrCallback instanceof Function ? optOrCallback : callback;
22-
const options: string | WriteFileOptions | undefined =
23+
const options: Encodings | WriteFileOptions | undefined =
2324
optOrCallback instanceof Function ? undefined : optOrCallback;
2425

2526
if (!callbackFn) {
@@ -71,7 +72,7 @@ export function writeFile(
7172
export function writeFileSync(
7273
pathOrRid: string | number | URL,
7374
data: string | Uint8Array,
74-
options?: string | WriteFileOptions
75+
options?: Encodings | WriteFileOptions
7576
): void {
7677
pathOrRid = pathOrRid instanceof URL ? fromFileUrl(pathOrRid) : pathOrRid;
7778

node/_fs/_fs_writeFile_test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Deno.test("Callback must be a function error", function fn() {
2424
Deno.test("Invalid encoding results in error()", function testEncodingErrors() {
2525
assertThrows(
2626
() => {
27+
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
2728
writeFile("some/path", "some data", "made-up-encoding", () => {});
2829
},
2930
Error,
@@ -32,6 +33,7 @@ Deno.test("Invalid encoding results in error()", function testEncodingErrors() {
3233

3334
assertThrows(
3435
() => {
36+
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
3537
writeFileSync("some/path", "some data", "made-up-encoding");
3638
},
3739
Error,
@@ -44,6 +46,7 @@ Deno.test("Invalid encoding results in error()", function testEncodingErrors() {
4446
"some/path",
4547
"some data",
4648
{
49+
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
4750
encoding: "made-up-encoding",
4851
},
4952
() => {}
@@ -56,6 +59,7 @@ Deno.test("Invalid encoding results in error()", function testEncodingErrors() {
5659
assertThrows(
5760
() => {
5861
writeFileSync("some/path", "some data", {
62+
// @ts-expect-error Type '"made-up-encoding"' is not assignable to type
5963
encoding: "made-up-encoding",
6064
});
6165
},

node/_fs/promises/_fs_readFile.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
2-
import { FileOptions } from "../_fs_common.ts";
3-
import { MaybeEmpty } from "../../_utils.ts";
4-
2+
import {
3+
FileOptionsArgument,
4+
BinaryOptionsArgument,
5+
TextOptionsArgument,
6+
} from "../_fs_common.ts";
57
import { readFile as readFileCallback } from "../_fs_readFile.ts";
68

79
export function readFile(
810
path: string | URL,
9-
options?: FileOptions | string
10-
): Promise<MaybeEmpty<string | Uint8Array>> {
11+
options: TextOptionsArgument
12+
): Promise<string>;
13+
export function readFile(
14+
path: string | URL,
15+
options?: BinaryOptionsArgument
16+
): Promise<Uint8Array>;
17+
export function readFile(
18+
path: string | URL,
19+
options?: FileOptionsArgument
20+
): Promise<string | Uint8Array> {
1121
return new Promise((resolve, reject) => {
1222
readFileCallback(path, options, (err, data): void => {
1323
if (err) return reject(err);
24+
if (data == null)
25+
return reject(new Error("Invalid state: data missing, but no error"));
1426
resolve(data);
1527
});
1628
});

0 commit comments

Comments
 (0)