Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fs: introduce readJSON functions #37944

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions doc/api/fs.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,33 @@ If one or more `filehandle.read()` calls are made on a file handle and then a
position till the end of the file. It doesn't always read from the beginning
of the file.

#### `filehandle.readJSON(options)`
targos marked this conversation as resolved.
Show resolved Hide resolved
<!-- YAML
added: REPLACEME
-->

* `options` {Object|string}
* `encoding` {string|null} **Default:** `'utf8'`
* `reviver` {Function} Reviver function passed to [`JSON.parse`][].
* `signal` {AbortSignal} allows aborting an in-progress readJSON
* Returns: {Promise} Fulfills upon a successful read with the contents of the
file parsed with [`JSON.parse`][].

Asynchronously reads the contents of a file and call [`JSON.parse`][] on it.

The `encoding` and `signal` options are passed to the underlying
[`fsPromises.readFile()`][] call and the `reviver` option is passed to
[`JSON.parse`][].

If `options` is a string, then it specifies the `encoding`.

The {FileHandle} has to support reading.

If one or more `filehandle.read()` calls are made on a file handle and then a
`filehandle.readJSON()` call is made, the data will be read from the current
position till the end of the file. It doesn't always read from the beginning
of the file.
targos marked this conversation as resolved.
Show resolved Hide resolved

#### `filehandle.readv(buffers[, position])`
<!-- YAML
added:
Expand Down Expand Up @@ -987,6 +1014,39 @@ system requests but rather the internal buffering `fs.readFile` performs.

Any specified {FileHandle} has to support reading.

### `fsPromises.readJSON(path[, options])`
<!-- YAML
added: REPLACEME
-->

* `path` {string|Buffer|URL|FileHandle} filename or `FileHandle`
* `options` {Object|string}
* `encoding` {string|null} **Default:** `'utf8'`
* `flag` {string} See [support of file system `flags`][]. **Default:** `'r'`.
* `reviver` {Function} Reviver function passed to [`JSON.parse`][].
* `signal` {AbortSignal} allows aborting an in-progress readFile
* Returns: {Promise} Fulfills with the contents of the file parsed with
[`JSON.parse`][].

Asynchronously reads the contents of a file and call [`JSON.parse`][] on it.

The `path` parameter, and the `encoding`, `flag` and `signal` options are passed
to the underlying [`fsPromises.readFile()`][] call and the `reviver` option is
passed to [`JSON.parse`][].

If `options` is a string, then it specifies the encoding.

It is possible to abort an ongoing `readJSON` using an {AbortSignal}. If a
request is aborted the promise returned is rejected with an `AbortError`.

Any specified {FileHandle} has to support reading.

```mjs
import { readJSON } from 'fs/promises';

const json = await readJSON('package.json');
```

### `fsPromises.readlink(path[, options])`
<!-- YAML
added: v10.0.0
Expand Down Expand Up @@ -2952,6 +3012,35 @@ The Node.js GitHub issue [#25741][] provides more information and a detailed
analysis on the performance of `fs.readFile()` for multiple file sizes in
different Node.js versions.

### `fs.readJSON(path[, options], callback)`
<!-- YAML
added: REPLACEME
-->

* `path` {string|Buffer|URL|integer} filename or file descriptor
* `options` {Object|string}
* `encoding` {string|null} **Default:** `'utf8'`
* `flag` {string} See [support of file system `flags`][]. **Default:** `'r'`.
* `reviver` {Function} Reviver function passed to [`JSON.parse`][].
* `signal` {AbortSignal} allows aborting an in-progress readJSON
* `callback` {Function}
* `err` {Error|AggregateError}
* `data` {any}

Asynchronously reads the contents of a file and call [`JSON.parse`][] on it.

The `path` parameter, and the `encoding`, `flag` and `signal` options are passed
to the underlying [`fs.readFileSync()`][] call and the `reviver` option is
passed to to [`JSON.parse`][].

For detailed information, see the documentation of [`fs.readFile()`][].

```mjs
import { readJSON } from 'fs';

readJSON('package.json', callback);
```

### `fs.readlink(path[, options], callback)`
<!-- YAML
added: v0.1.31
Expand Down Expand Up @@ -4618,6 +4707,33 @@ readFileSync('<directory>');
readFileSync('<directory>'); // => <data>
```

### `fs.readJSONSync(path[, options])`
<!-- YAML
added: REPLACEME
-->

* `path` {string|Buffer|URL|integer} filename or file descriptor
* `options` {Object|string}
* `encoding` {string|null} **Default:** `'utf8'`
* `flag` {string} See [support of file system `flags`][]. **Default:** `'r'`.
* `reviver` {Function} Reviver function passed to [`JSON.parse`][].
* Returns: {any}

Reads the contents of the `path` and returns the result of calling
[`JSON.parse`][] on it.

The `path` parameter, and the `encoding` and `flag` options are passed to the
underlying [`fs.readFileSync()`][] call and the `reviver` option is passed to
to [`JSON.parse`][].

For detailed information, see the documentation of [`fs.readFile()`][].

```mjs
import { readJSONSync } from 'fs';

const json = readJSONSync('package.json');
```

### `fs.readlinkSync(path[, options])`
<!-- YAML
added: v0.1.31
Expand Down Expand Up @@ -6659,6 +6775,7 @@ the file contents.
[`AHAFS`]: https://www.ibm.com/developerworks/aix/library/au-aix_event_infrastructure/
[`Buffer.byteLength`]: buffer.md#buffer_static_method_buffer_bytelength_string_encoding
[`FSEvents`]: https://developer.apple.com/documentation/coreservices/file_system_events
[`JSON.Parse`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
[`Number.MAX_SAFE_INTEGER`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
[`ReadDirectoryChangesW`]: https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-readdirectorychangesw
[`UV_THREADPOOL_SIZE`]: cli.md#cli_uv_threadpool_size_size
Expand Down Expand Up @@ -6701,6 +6818,7 @@ the file contents.
[`fs.writev()`]: #fs_fs_writev_fd_buffers_position_callback
[`fsPromises.open()`]: #fs_fspromises_open_path_flags_mode
[`fsPromises.opendir()`]: #fs_fspromises_opendir_path_options
[`fsPromises.readFile()`]: #fs_fspromises_readfile_path_options
[`fsPromises.rm()`]: #fs_fspromises_rm_path_options
[`fsPromises.utimes()`]: #fs_fspromises_utimes_path_atime_mtime
[`inotify(7)`]: https://man7.org/linux/man-pages/man7/inotify.7.html
Expand Down
35 changes: 35 additions & 0 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const kIoMaxLength = 2 ** 31 - 1;
const {
ArrayPrototypePush,
BigIntPrototypeToString,
JSONParse,
MathMax,
Number,
ObjectCreate,
Expand Down Expand Up @@ -360,6 +361,27 @@ function readFile(path, options, callback) {
req);
}

function readJSON(path, options, callback) {
callback = maybeCallback(callback || options);
options = getOptions(options, { encoding: 'utf8', flag: 'r' });

const { reviver } = options;
if (typeof reviver !== 'undefined') {
validateFunction(reviver, 'options.reviver');
}

readFile(path, options, (readFileError, file) => {
if (readFileError) return callback(readFileError);
let json;
try {
json = JSONParse(file, reviver);
} catch (parseError) {
return callback(parseError);
}
callback(null, json);
});
}

function tryStatSync(fd, isUserFd) {
const ctx = {};
const stats = binding.fstat(fd, false, undefined, ctx);
Expand Down Expand Up @@ -448,6 +470,17 @@ function readFileSync(path, options) {
return buffer;
}

function readJSONSync(path, options) {
options = getOptions(options, { encoding: 'utf8', flag: 'r' });

const { reviver } = options;
if (typeof reviver !== 'undefined') {
validateFunction(reviver, 'options.reviver');
}

return JSONParse(readFileSync(path, options), reviver);
}

function defaultCloseCallback(err) {
if (err != null) throw err;
}
Expand Down Expand Up @@ -2157,6 +2190,8 @@ module.exports = fs = {
readvSync,
readFile,
readFileSync,
readJSON,
readJSONSync,
readlink,
readlinkSync,
realpath,
Expand Down
19 changes: 19 additions & 0 deletions lib/internal/fs/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const kWriteFileMaxChunkSize = 512 * 1024;
const {
ArrayPrototypePush,
Error,
JSONParse,
MathMax,
MathMin,
NumberIsSafeInteger,
Expand Down Expand Up @@ -71,6 +72,7 @@ const {
validateAbortSignal,
validateBoolean,
validateBuffer,
validateFunction,
validateInteger,
validateUint32
} = require('internal/validators');
Expand Down Expand Up @@ -146,6 +148,10 @@ class FileHandle extends EventEmitterMixin(JSTransferable) {
return fsCall(readFile, this, options);
}

readJSON(options) {
return fsCall(readJSON, this, options);
}

stat(options) {
return fsCall(fstat, this, options);
}
Expand Down Expand Up @@ -718,6 +724,18 @@ async function readFile(path, options) {
return PromisePrototypeFinally(readFileHandle(fd, options), fd.close);
}

async function readJSON(path, options) {
options = getOptions(options, { encoding: 'utf8', flag: 'r' });

const { reviver } = options;
if (typeof reviver !== 'undefined') {
validateFunction(reviver, 'options.reviver');
}

const file = await readFile(path, options);
return JSONParse(file, reviver);
}

module.exports = {
exports: {
access,
Expand Down Expand Up @@ -747,6 +765,7 @@ module.exports = {
writeFile,
appendFile,
readFile,
readJSON,
watch,
},

Expand Down
3 changes: 1 addition & 2 deletions lib/internal/source_map/source_map_cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,7 @@ function lineLengths(content) {

function sourceMapFromFile(mapURL) {
try {
const content = fs.readFileSync(fileURLToPath(mapURL), 'utf8');
const data = JSONParse(content);
const data = fs.readJSONSync(fileURLToPath(mapURL));
return sourcesToAbsolute(mapURL, data);
} catch (err) {
debug(err.stack);
Expand Down
2 changes: 1 addition & 1 deletion test/common/wpt.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ class StatusLoader {
load() {
const dir = path.join(__dirname, '..', 'wpt');
const statusFile = path.join(dir, 'status', `${this.path}.json`);
const result = JSON.parse(fs.readFileSync(statusFile, 'utf8'));
const result = fs.readJSONSync(statusFile);
this.rules.addRules(result);

const subDir = fixtures.path('wpt', this.path);
Expand Down
3 changes: 1 addition & 2 deletions test/internet/test-trace-events-dns.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ for (const tr in tests) {

const file = path.join(tmpdir.path, traceFile);

const data = fs.readFileSync(file);
const traces = JSON.parse(data.toString()).traceEvents
const traces = fs.readJSONSync(file).traceEvents
.filter((trace) => trace.cat !== '__metadata');

assert(traces.length > 0);
Expand Down
Loading