From bca7ccff15de7e60ffbd32f120939fb278103e14 Mon Sep 17 00:00:00 2001 From: Miguel Jimenez Esun Date: Sat, 17 Feb 2018 19:16:32 +0000 Subject: [PATCH 1/3] Add jest-serializer module --- packages/jest-cli/src/search_source.js | 1 - packages/jest-haste-map/package.json | 1 + packages/jest-haste-map/src/index.js | 42 ++----- packages/jest-serializer/.npmignore | 3 + packages/jest-serializer/README.md | 86 ++++++++++++++ packages/jest-serializer/package.json | 13 +++ .../src/__tests__/index.test.js | 92 +++++++++++++++ packages/jest-serializer/src/index.js | 107 ++++++++++++++++++ 8 files changed, 311 insertions(+), 34 deletions(-) create mode 100644 packages/jest-serializer/.npmignore create mode 100644 packages/jest-serializer/README.md create mode 100644 packages/jest-serializer/package.json create mode 100644 packages/jest-serializer/src/__tests__/index.test.js create mode 100644 packages/jest-serializer/src/index.js diff --git a/packages/jest-cli/src/search_source.js b/packages/jest-cli/src/search_source.js index 51fff5854b67..1b5c7e06039b 100644 --- a/packages/jest-cli/src/search_source.js +++ b/packages/jest-cli/src/search_source.js @@ -12,7 +12,6 @@ import type {Glob, GlobalConfig, Path} from 'types/Config'; import type {Test} from 'types/TestRunner'; import type {ChangedFilesPromise} from 'types/ChangedFiles'; -import fs from 'fs'; import path from 'path'; import micromatch from 'micromatch'; import DependencyResolver from 'jest-resolve-dependencies'; diff --git a/packages/jest-haste-map/package.json b/packages/jest-haste-map/package.json index 888e79f41a23..1520b47d69bb 100644 --- a/packages/jest-haste-map/package.json +++ b/packages/jest-haste-map/package.json @@ -11,6 +11,7 @@ "fb-watchman": "^2.0.0", "graceful-fs": "^4.1.11", "jest-docblock": "^22.2.2", + "jest-serializer": "^22.3.0", "jest-worker": "^22.2.2", "micromatch": "^2.3.11", "sane": "^2.0.0" diff --git a/packages/jest-haste-map/src/index.js b/packages/jest-haste-map/src/index.js index c990ea92f9ae..4af29b1c25ef 100644 --- a/packages/jest-haste-map/src/index.js +++ b/packages/jest-haste-map/src/index.js @@ -12,7 +12,6 @@ import {version as VERSION} from '../package.json'; import {worker} from './worker'; import crypto from 'crypto'; import EventEmitter from 'events'; -import fs from 'graceful-fs'; import getMockName from './get_mock_name'; import getPlatformExtension from './lib/get_platform_extension'; // eslint-disable-next-line import/no-duplicates @@ -25,7 +24,7 @@ import normalizePathSep from './lib/normalize_path_sep'; import os from 'os'; import path from 'path'; import sane from 'sane'; -import v8 from 'v8'; +import serializer from 'jest-serializer'; // eslint-disable-next-line import/default import watchmanCrawl from './crawlers/watchman'; import WatchmanWatcher from './lib/watchman_watcher'; @@ -290,21 +289,14 @@ class HasteMap extends EventEmitter { * 1. read data from the cache or create an empty structure. */ read(): InternalHasteMap { - if (v8.deserialize) { - // This may throw. `_buildFileMap` will catch it and create a new map. - const {version, hasteMap} = v8.deserialize( - fs.readFileSync(this._cachePath), - ); - if (version !== process.versions.v8) { - throw new Error('jest-haste-map: v8 versions do not match.'); - } - return removePrototypes(hasteMap); - } else { - const hasteMap = (JSON.parse( - fs.readFileSync(this._cachePath, 'utf8'), - ): InternalHasteMap); - return removePrototypes(hasteMap); + // This may throw. `_buildFileMap` will catch it and create a new map. + const hasteMap: InternalHasteMap = serializer.readFileSync(this._cachePath); + + for (const key in hasteMap) { + Object.setPrototypeOf(hasteMap[key], null); } + + return hasteMap; } readModuleMap(): ModuleMap { @@ -534,17 +526,7 @@ class HasteMap extends EventEmitter { * 4. serialize the new `HasteMap` in a cache file. */ _persist(hasteMap: InternalHasteMap) { - if (v8.serialize) { - fs.writeFileSync( - this._cachePath, - v8.serialize({ - hasteMap, - version: process.versions.v8, - }), - ); - } else { - fs.writeFileSync(this._cachePath, JSON.stringify(hasteMap), 'utf8'); - } + serializer.writeFileSync(this._cachePath, hasteMap); } /** @@ -913,12 +895,6 @@ class HasteMap extends EventEmitter { } const copy = object => Object.assign(Object.create(null), object); -const removePrototypes = object => { - for (const key in object) { - Object.setPrototypeOf(object[key], null); - } - return object; -}; HasteMap.H = H; HasteMap.ModuleMap = HasteModuleMap; diff --git a/packages/jest-serializer/.npmignore b/packages/jest-serializer/.npmignore new file mode 100644 index 000000000000..85e48fe7b0a4 --- /dev/null +++ b/packages/jest-serializer/.npmignore @@ -0,0 +1,3 @@ +**/__mocks__/** +**/__tests__/** +src diff --git a/packages/jest-serializer/README.md b/packages/jest-serializer/README.md new file mode 100644 index 000000000000..f5cb3c5a5504 --- /dev/null +++ b/packages/jest-serializer/README.md @@ -0,0 +1,86 @@ +# jest-serializer + +Module for serializing and deserializing object into memory and disk. By +default, the `v8` implementations are used, but if not present, it defaults to +`JSON` implementation. The V8 serializer has the advantage of being able to +serialize `Map`, `Set`, `undefined`, circular references, etc. It is also a +more compact format to be stored and sent over the wire. + +## Install + +```sh +$ yarn add jest-serializer +``` + +## API + +Three kinds of API groups are exposed: + +### In-memory serialization: `serialize` and `deserialize` + +This set of functions take or return a `Buffer`. All the process happens in +memory. This is useful when willing to transfer over HTTP, TCP or via UNIX +pipes. + +```javascript +import serializer from 'jest-serializer'; + +const myObject = { + foo: 'bar', + baz: [0, true, '2', [], {}], +}; + +const buffer = serializer.serialize(myObject); +const myCopyObject = serializer.deserialize(buffer); +``` + +### Synchronous persistent filesystem: `readFileSync` and `writeFileSync` + +This set of functions allow to send to disk a serialization result and retrieve +it back, in a synchronous way. It mimics the `fs` API so it looks familiar. + +```javascript +import serializer from 'jest-serializer'; + +const myObject = { + foo: 'bar', + baz: [0, true, '2', [], {}], +}; + +const myFile = '/tmp/obj'; + +serializer.writeFileSync(myFile, myObject); +const myCopyObject = serializer.readFileSync(myFile); +``` + +### Asynchronous persistent filesystem: `readFile` and `writeFile` + +Pretty similar to the synchronous one, but providing a callback. It also mimics +the `fs` API. + +```javascript +import serializer from 'jest-serializer'; + +const myObject = { + foo: 'bar', + baz: [0, true, '2', [], {}], +}; + +const myFile = '/tmp/obj'; + +serializer.writeFile(myFile, myObject, (err) => { + if (err) { + console.error(err); + return; + } + + serializer.readFile(myFile, (err, data) => { + if (err) { + console.error(err); + return; + } + + const myCopyObject = data; + }); +}); +``` diff --git a/packages/jest-serializer/package.json b/packages/jest-serializer/package.json new file mode 100644 index 000000000000..7b0c05e0d457 --- /dev/null +++ b/packages/jest-serializer/package.json @@ -0,0 +1,13 @@ +{ + "name": "jest-serializer", + "version": "22.3.0", + "repository": { + "type": "git", + "url": "https://github.com/facebook/jest.git" + }, + "license": "MIT", + "main": "build/index.js", + "dependencies": { + "merge-stream": "^1.0.1" + } +} diff --git a/packages/jest-serializer/src/__tests__/index.test.js b/packages/jest-serializer/src/__tests__/index.test.js new file mode 100644 index 000000000000..916649159318 --- /dev/null +++ b/packages/jest-serializer/src/__tests__/index.test.js @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2018-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +import fs from 'fs'; +import os from 'os'; +import path from 'path'; +import v8 from 'v8'; + +import serializer from '..'; + +const v8s = [ + { + deserialize: v8.deserialize, + serialize: v8.serialize, + }, + { + deserialize: undefined, + serialize: undefined, + }, +]; + +const objs = [ + 3, + null, + [0, true, '2', [3.14, {}, null]], + {key1: 'foo', key2: 'bar', key3: {array: [null, {}]}}, +]; + +const file = path.join(os.tmpdir(), '__jest-serialize-test__'); + +afterEach(() => { + try { + fs.unlinkSync(file); + } catch (err) { + // Do nothing if file does not exist. + } +}); + +// We execute the same suite of tests over multiple objects ("objs") and over +// multiple mocks of the V8 object ("v8s") so that we verify that all possible +// encodings and cases work. +v8s.forEach((mockV8, i) => { + describe('Using V8 implementation ' + i, () => { + jest.mock('v8', () => mockV8); + + it('throws the error with an invalid serialization', done => { + // No chance this is a valid serialization, neither in JSON nor V8. + const invalidBuffer = Buffer.from([0x00, 0x55, 0xaa, 0xff]); + + fs.writeFileSync(file, invalidBuffer); + + expect(() => serializer.deserialize(invalidBuffer)).toThrow(); + expect(() => serializer.readFileSync(file)).toThrow(); + + serializer.readFile(file, err => { + expect(err).toBeDefined(); + done(); + }); + }); + + objs.forEach((obj, i) => { + describe('Object ' + i, () => { + it('serializes/deserializes in memory', () => { + const buf = serializer.serialize(obj); + + expect(buf).toBeInstanceOf(Buffer); + expect(serializer.deserialize(buf)).toEqual(obj); + }); + + it('serializes/deserializes in disk, synchronously', () => { + serializer.writeFileSync(file, obj); + expect(serializer.readFileSync(file)).toEqual(obj); + }); + + it('serializes/deserializes in disk, asynchronously', done => { + serializer.writeFile(file, obj, err => { + serializer.readFile(file, (err, data) => { + expect(data).toEqual(obj); + done(); + }); + }); + }); + }); + }); + }); +}); diff --git a/packages/jest-serializer/src/index.js b/packages/jest-serializer/src/index.js new file mode 100644 index 000000000000..2413ca6e19ad --- /dev/null +++ b/packages/jest-serializer/src/index.js @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2018-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +'use strict'; + +import fs from 'fs'; +import v8 from 'v8'; + +import type {Path} from 'types/Config'; + +type IOCallback = (?Error, ?any) => void; + +// JSON and V8 serializers are both stable when it comes to compatibility. The +// current JSON specification is well defined in RFC 8259, and V8 ensures that +// the versions are compatible by encoding the serialization version in the own +// generated buffer. + +// In memory functions. + +export function serialize(content: any): Buffer { + return v8.serialize + ? v8.serialize(content) + : Buffer.from(JSON.stringify(content)); +} + +export function deserialize(buffer: Buffer): any { + return v8.deserialize + ? v8.deserialize(buffer) + : Buffer.from(JSON.stringify(buffer.toString('utf8'))); +} + +// Synchronous filesystem functions. + +export function readFileSync(file: Path): any { + return v8.deserialize + ? v8.deserialize(fs.readFileSync(file)) + : JSON.parse(fs.readFileSync(file, 'utf8')); +} + +export function writeFileSync(file: Path, content: any) { + return v8.serialize + ? fs.writeFileSync(file, v8.serialize(content)) + : fs.writeFileSync(file, JSON.stringify(content), 'utf8'); +} + +// Asynchronous filesystem functions. + +export function readFile(file: Path, callback: IOCallback) { + if (v8.deserialize) { + fs.readFile(file, (err, data) => { + if (err) { + callback(err); + return; + } + + try { + callback(null, v8.deserialize(data)); + } catch (error) { + callback(error); + } + }); + } else { + fs.readFile(file, 'utf8', (err, data) => { + if (err) { + callback(err); + return; + } + + try { + callback(null, JSON.parse(data)); + } catch (error) { + callback(error); + } + }); + } +} + +export function writeFile(file: Path, content: any, callback: IOCallback) { + if (v8.serialize) { + try { + fs.writeFile(file, v8.serialize(content), callback); + } catch (err) { + callback(err); + } + } else { + try { + fs.writeFile(file, JSON.stringify(content), callback); + } catch (err) { + callback(err); + } + } +} + +export default { + deserialize, + readFile, + readFileSync, + serialize, + writeFile, + writeFileSync, +}; From ea468e53a3c4882b36fc9970fed755ca48b752a9 Mon Sep 17 00:00:00 2001 From: Miguel Jimenez Esun Date: Mon, 19 Feb 2018 12:25:13 +0000 Subject: [PATCH 2/3] Address feedback: "package.json" + "README.md" --- packages/jest-serializer/README.md | 18 +++++++++--------- packages/jest-serializer/package.json | 5 +---- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/jest-serializer/README.md b/packages/jest-serializer/README.md index f5cb3c5a5504..ef76bc911245 100644 --- a/packages/jest-serializer/README.md +++ b/packages/jest-serializer/README.md @@ -23,15 +23,15 @@ memory. This is useful when willing to transfer over HTTP, TCP or via UNIX pipes. ```javascript -import serializer from 'jest-serializer'; +import {serialize, deserialize} serializer from 'jest-serializer'; const myObject = { foo: 'bar', baz: [0, true, '2', [], {}], }; -const buffer = serializer.serialize(myObject); -const myCopyObject = serializer.deserialize(buffer); +const buffer = serialize(myObject); +const myCopyObject = deserialize(buffer); ``` ### Synchronous persistent filesystem: `readFileSync` and `writeFileSync` @@ -40,7 +40,7 @@ This set of functions allow to send to disk a serialization result and retrieve it back, in a synchronous way. It mimics the `fs` API so it looks familiar. ```javascript -import serializer from 'jest-serializer'; +import {readFileSync, writeFileSync} serializer from 'jest-serializer'; const myObject = { foo: 'bar', @@ -49,8 +49,8 @@ const myObject = { const myFile = '/tmp/obj'; -serializer.writeFileSync(myFile, myObject); -const myCopyObject = serializer.readFileSync(myFile); +writeFileSync(myFile, myObject); +const myCopyObject = readFileSync(myFile); ``` ### Asynchronous persistent filesystem: `readFile` and `writeFile` @@ -59,7 +59,7 @@ Pretty similar to the synchronous one, but providing a callback. It also mimics the `fs` API. ```javascript -import serializer from 'jest-serializer'; +import {readFile, writeFile} from 'jest-serializer'; const myObject = { foo: 'bar', @@ -68,13 +68,13 @@ const myObject = { const myFile = '/tmp/obj'; -serializer.writeFile(myFile, myObject, (err) => { +writeFile(myFile, myObject, (err) => { if (err) { console.error(err); return; } - serializer.readFile(myFile, (err, data) => { + readFile(myFile, (err, data) => { if (err) { console.error(err); return; diff --git a/packages/jest-serializer/package.json b/packages/jest-serializer/package.json index 7b0c05e0d457..a3e797ecae13 100644 --- a/packages/jest-serializer/package.json +++ b/packages/jest-serializer/package.json @@ -6,8 +6,5 @@ "url": "https://github.com/facebook/jest.git" }, "license": "MIT", - "main": "build/index.js", - "dependencies": { - "merge-stream": "^1.0.1" - } + "main": "build/index.js" } From c715d6fb1348b882fce422f2a17a0e045df95179 Mon Sep 17 00:00:00 2001 From: Miguel Jimenez Esun Date: Mon, 19 Feb 2018 15:21:34 +0000 Subject: [PATCH 3/3] Address feedback: allow JSON stringification to handle native types --- packages/jest-serializer/README.md | 42 +--- .../src/__tests__/index.test.js | 39 ++-- packages/jest-serializer/src/index.js | 186 ++++++++++++------ 3 files changed, 147 insertions(+), 120 deletions(-) diff --git a/packages/jest-serializer/README.md b/packages/jest-serializer/README.md index ef76bc911245..6c8a4a4c9e11 100644 --- a/packages/jest-serializer/README.md +++ b/packages/jest-serializer/README.md @@ -2,9 +2,9 @@ Module for serializing and deserializing object into memory and disk. By default, the `v8` implementations are used, but if not present, it defaults to -`JSON` implementation. The V8 serializer has the advantage of being able to -serialize `Map`, `Set`, `undefined`, circular references, etc. It is also a -more compact format to be stored and sent over the wire. +`JSON` implementation. Both serializers have the advantage of being able to +serialize `Map`, `Set`, `undefined`, `NaN`, etc, although the JSON one does it +through a replacer/reviver. ## Install @@ -23,7 +23,7 @@ memory. This is useful when willing to transfer over HTTP, TCP or via UNIX pipes. ```javascript -import {serialize, deserialize} serializer from 'jest-serializer'; +import {serialize, deserialize} from 'jest-serializer'; const myObject = { foo: 'bar', @@ -40,7 +40,7 @@ This set of functions allow to send to disk a serialization result and retrieve it back, in a synchronous way. It mimics the `fs` API so it looks familiar. ```javascript -import {readFileSync, writeFileSync} serializer from 'jest-serializer'; +import {readFileSync, writeFileSync} from 'jest-serializer'; const myObject = { foo: 'bar', @@ -52,35 +52,3 @@ const myFile = '/tmp/obj'; writeFileSync(myFile, myObject); const myCopyObject = readFileSync(myFile); ``` - -### Asynchronous persistent filesystem: `readFile` and `writeFile` - -Pretty similar to the synchronous one, but providing a callback. It also mimics -the `fs` API. - -```javascript -import {readFile, writeFile} from 'jest-serializer'; - -const myObject = { - foo: 'bar', - baz: [0, true, '2', [], {}], -}; - -const myFile = '/tmp/obj'; - -writeFile(myFile, myObject, (err) => { - if (err) { - console.error(err); - return; - } - - readFile(myFile, (err, data) => { - if (err) { - console.error(err); - return; - } - - const myCopyObject = data; - }); -}); -``` diff --git a/packages/jest-serializer/src/__tests__/index.test.js b/packages/jest-serializer/src/__tests__/index.test.js index 916649159318..1ae9b70fe9fa 100644 --- a/packages/jest-serializer/src/__tests__/index.test.js +++ b/packages/jest-serializer/src/__tests__/index.test.js @@ -7,6 +7,8 @@ 'use strict'; +import prettyFormat from 'pretty-format'; + import fs from 'fs'; import os from 'os'; import path from 'path'; @@ -30,6 +32,10 @@ const objs = [ null, [0, true, '2', [3.14, {}, null]], {key1: 'foo', key2: 'bar', key3: {array: [null, {}]}}, + {minusInf: -Infinity, nan: NaN, plusInf: +Infinity}, + {date: new Date(1234567890), re: /foo/gi}, + {map: new Map([[NaN, 4], [undefined, 'm']]), set: new Set([undefined, NaN])}, + {buf: Buffer.from([0, 255, 127])}, ]; const file = path.join(os.tmpdir(), '__jest-serialize-test__'); @@ -47,21 +53,19 @@ afterEach(() => { // encodings and cases work. v8s.forEach((mockV8, i) => { describe('Using V8 implementation ' + i, () => { - jest.mock('v8', () => mockV8); + beforeEach(() => { + v8.serialize = mockV8.serialize; + v8.deserialize = mockV8.deserialize; + }); - it('throws the error with an invalid serialization', done => { + it('throws the error with an invalid serialization', () => { // No chance this is a valid serialization, neither in JSON nor V8. - const invalidBuffer = Buffer.from([0x00, 0x55, 0xaa, 0xff]); + const invalidBuffer = Buffer.from([0, 85, 170, 255]); fs.writeFileSync(file, invalidBuffer); expect(() => serializer.deserialize(invalidBuffer)).toThrow(); expect(() => serializer.readFileSync(file)).toThrow(); - - serializer.readFile(file, err => { - expect(err).toBeDefined(); - done(); - }); }); objs.forEach((obj, i) => { @@ -70,21 +74,18 @@ v8s.forEach((mockV8, i) => { const buf = serializer.serialize(obj); expect(buf).toBeInstanceOf(Buffer); - expect(serializer.deserialize(buf)).toEqual(obj); + + expect(prettyFormat(serializer.deserialize(buf))).toEqual( + prettyFormat(obj), + ); }); - it('serializes/deserializes in disk, synchronously', () => { + it('serializes/deserializes in disk', () => { serializer.writeFileSync(file, obj); - expect(serializer.readFileSync(file)).toEqual(obj); - }); - it('serializes/deserializes in disk, asynchronously', done => { - serializer.writeFile(file, obj, err => { - serializer.readFile(file, (err, data) => { - expect(data).toEqual(obj); - done(); - }); - }); + expect(prettyFormat(serializer.readFileSync(file))).toEqual( + prettyFormat(obj), + ); }); }); }); diff --git a/packages/jest-serializer/src/index.js b/packages/jest-serializer/src/index.js index 2413ca6e19ad..ff40f1c3724c 100644 --- a/packages/jest-serializer/src/index.js +++ b/packages/jest-serializer/src/index.js @@ -14,94 +14,152 @@ import v8 from 'v8'; import type {Path} from 'types/Config'; -type IOCallback = (?Error, ?any) => void; - // JSON and V8 serializers are both stable when it comes to compatibility. The // current JSON specification is well defined in RFC 8259, and V8 ensures that // the versions are compatible by encoding the serialization version in the own // generated buffer. -// In memory functions. +const JS_TYPE = '__$t__'; +const JS_VALUE = '__$v__'; +const JS_VF = '__$f__'; -export function serialize(content: any): Buffer { - return v8.serialize - ? v8.serialize(content) - : Buffer.from(JSON.stringify(content)); +function replacer(key: string, value: any): any { + // NaN cannot be in a switch statement, because NaN !== NaN. + if (Number.isNaN(value)) { + return {[JS_TYPE]: 'n'}; + } + + switch (value) { + case undefined: + return {[JS_TYPE]: 'u'}; + + case +Infinity: + return {[JS_TYPE]: '+'}; + + case -Infinity: + return {[JS_TYPE]: '-'}; + } + + switch (value && value.constructor) { + case Date: + return {[JS_TYPE]: 'd', [JS_VALUE]: value.getTime()}; + + case RegExp: + return {[JS_TYPE]: 'r', [JS_VALUE]: value.source, [JS_VF]: value.flags}; + + case Set: + return {[JS_TYPE]: 's', [JS_VALUE]: Array.from(value)}; + + case Map: + return {[JS_TYPE]: 'm', [JS_VALUE]: Array.from(value)}; + + case Buffer: + return {[JS_TYPE]: 'b', [JS_VALUE]: value.toString('latin1')}; + } + + return value; } -export function deserialize(buffer: Buffer): any { - return v8.deserialize - ? v8.deserialize(buffer) - : Buffer.from(JSON.stringify(buffer.toString('utf8'))); +function reviver(key: string, value: any): any { + if (!value || (typeof value !== 'object' && !value.hasOwnProperty(JS_TYPE))) { + return value; + } + + switch (value[JS_TYPE]) { + case 'u': + return undefined; + + case 'n': + return NaN; + + case '+': + return +Infinity; + + case '-': + return -Infinity; + + case 'd': + return new Date(value[JS_VALUE]); + + case 'r': + return new RegExp(value[JS_VALUE], value[JS_VF]); + + case 's': + return new Set(value[JS_VALUE]); + + case 'm': + return new Map(value[JS_VALUE]); + + case 'b': + return Buffer.from(value[JS_VALUE], 'latin1'); + } + + return value; } -// Synchronous filesystem functions. +function jsonStringify(content) { + // Not pretty, but the ES JSON spec says that "toJSON" will be called before + // getting into your replacer, so we have to remove them beforehand. See + // https://www.ecma-international.org/ecma-262/#sec-serializejsonproperty + // section 2.b for more information. + + const dateToJSON = Date.prototype.toJSON; + const bufferToJSON = Buffer.prototype.toJSON; + + /* eslint-disable no-extend-native */ + + try { + // $FlowFixMe: intentional removal of "toJSON" property. + Date.prototype.toJSON = undefined; + // $FlowFixMe: intentional removal of "toJSON" property. + Buffer.prototype.toJSON = undefined; + + return JSON.stringify(content, replacer); + } finally { + // $FlowFixMe: intentional assignment of "toJSON" property. + Date.prototype.toJSON = dateToJSON; + // $FlowFixMe: intentional assignment of "toJSON" property. + Buffer.prototype.toJSON = bufferToJSON; + } + + /* eslint-enable no-extend-native */ +} + +function jsonParse(content) { + return JSON.parse(content, reviver); +} -export function readFileSync(file: Path): any { +// In memory functions. + +export function deserialize(buffer: Buffer): any { return v8.deserialize - ? v8.deserialize(fs.readFileSync(file)) - : JSON.parse(fs.readFileSync(file, 'utf8')); + ? v8.deserialize(buffer) + : jsonParse(buffer.toString('utf8')); } -export function writeFileSync(file: Path, content: any) { +export function serialize(content: any): Buffer { return v8.serialize - ? fs.writeFileSync(file, v8.serialize(content)) - : fs.writeFileSync(file, JSON.stringify(content), 'utf8'); + ? v8.serialize(content) + : Buffer.from(jsonStringify(content)); } -// Asynchronous filesystem functions. - -export function readFile(file: Path, callback: IOCallback) { - if (v8.deserialize) { - fs.readFile(file, (err, data) => { - if (err) { - callback(err); - return; - } - - try { - callback(null, v8.deserialize(data)); - } catch (error) { - callback(error); - } - }); - } else { - fs.readFile(file, 'utf8', (err, data) => { - if (err) { - callback(err); - return; - } - - try { - callback(null, JSON.parse(data)); - } catch (error) { - callback(error); - } - }); - } +// Synchronous filesystem functions. + +export function readFileSync(filePath: Path): any { + return v8.deserialize + ? v8.deserialize(fs.readFileSync(filePath)) + : jsonParse(fs.readFileSync(filePath, 'utf8')); } -export function writeFile(file: Path, content: any, callback: IOCallback) { - if (v8.serialize) { - try { - fs.writeFile(file, v8.serialize(content), callback); - } catch (err) { - callback(err); - } - } else { - try { - fs.writeFile(file, JSON.stringify(content), callback); - } catch (err) { - callback(err); - } - } +export function writeFileSync(filePath: Path, content: any) { + return v8.serialize + ? fs.writeFileSync(filePath, v8.serialize(content)) + : fs.writeFileSync(filePath, jsonStringify(content), 'utf8'); } export default { deserialize, - readFile, readFileSync, serialize, - writeFile, writeFileSync, };