From 3080861d5894ef6039ae4456b5a32b03e22723ac Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 9 Dec 2020 02:52:09 +0000 Subject: [PATCH 1/8] Don't allocate the inner cache unnecessarily We only need it when we're asking for text. I anticipate I'll want to avoid allocating it in other methods too when it's not strictly necessary. --- packages/react-fs/src/ReactFilesystem.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/react-fs/src/ReactFilesystem.js b/packages/react-fs/src/ReactFilesystem.js index f13c6ce124760..b9d22c2c5ece6 100644 --- a/packages/react-fs/src/ReactFilesystem.js +++ b/packages/react-fs/src/ReactFilesystem.js @@ -20,19 +20,19 @@ const Rejected = 2; type PendingRecord = {| status: 0, value: Wakeable, - cache: Array, + cache: null, |}; type ResolvedRecord = {| status: 1, value: T, - cache: Array, + cache: null | Array, |}; type RejectedRecord = {| status: 2, value: mixed, - cache: Array, + cache: null, |}; type Record = PendingRecord | ResolvedRecord | RejectedRecord; @@ -41,7 +41,7 @@ function createRecordFromThenable(thenable: Thenable): Record { const record: Record = { status: Pending, value: thenable, - cache: [], + cache: null, }; thenable.then( value => { @@ -62,9 +62,10 @@ function createRecordFromThenable(thenable: Thenable): Record { return record; } -function readRecordValue(record: Record): T { +function readRecord(record: Record): ResolvedRecord { if (record.status === Resolved) { - return record.value; + // This is just a type refinement. + return record; } else { throw record.value; } @@ -114,7 +115,8 @@ export function readFile( record = createRecordFromThenable(thenable); map.set(path, record); } - const buffer: Buffer = readRecordValue(record); + const resolvedRecord = readRecord(record); + const buffer: Buffer = resolvedRecord.value; if (!options) { return buffer; } @@ -136,7 +138,7 @@ export function readFile( if (typeof encoding !== 'string') { return buffer; } - const textCache = record.cache; + const textCache = resolvedRecord.cache || (resolvedRecord.cache = []); for (let i = 0; i < textCache.length; i += 2) { if (textCache[i] === encoding) { return (textCache[i + 1]: any); From bc0984c1e6d9d8a090395e1ee34cc61929bca3f2 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 9 Dec 2020 03:55:24 +0000 Subject: [PATCH 2/8] Add fs.access --- packages/react-fs/src/ReactFilesystem.js | 34 +++++++++++++++++++++++- scripts/flow/environment.js | 1 + scripts/rollup/modules.js | 1 + 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/react-fs/src/ReactFilesystem.js b/packages/react-fs/src/ReactFilesystem.js index b9d22c2c5ece6..4e743c5e0c693 100644 --- a/packages/react-fs/src/ReactFilesystem.js +++ b/packages/react-fs/src/ReactFilesystem.js @@ -92,6 +92,38 @@ function checkPathInDev(path: string) { } } +function createAccessCache(): Map>> { + return new Map(); +} + +export function access(path: string, mode?: number): void { + checkPathInDev(path); + if (mode == null) { + mode = 0; // fs.constants.F_OK + } + const map = unstable_getCacheForType(createAccessCache); + let accessCache = map.get(path); + if (!accessCache) { + accessCache = []; + map.set(path, accessCache); + } + let record; + for (let i = 0; i < accessCache.length; i += 2) { + const cachedMode: number = (accessCache[i]: any); + if (mode === cachedMode) { + const cachedRecord: Record = (accessCache[i + 1]: any); + record = cachedRecord; + break; + } + } + if (!record) { + const thenable = fs.access(path, mode); + record = createRecordFromThenable(thenable); + accessCache.push(mode, record); + } + readRecord(record); // No return value. +} + function createReadFileCache(): Map> { return new Map(); } @@ -107,8 +139,8 @@ export function readFile( signal?: mixed, // We'll have our own signal }, ): string | Buffer { - const map = unstable_getCacheForType(createReadFileCache); checkPathInDev(path); + const map = unstable_getCacheForType(createReadFileCache); let record = map.get(path); if (!record) { const thenable = fs.readFile(path); diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index 0fac49adfb211..09dc8bfa9927f 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -71,6 +71,7 @@ declare function __webpack_chunk_load__(id: string): Promise; declare function __webpack_require__(id: string): any; declare module 'fs/promises' { + declare var access: (path: string, mode?: number) => Promise; declare var readFile: ( path: string, options?: diff --git a/scripts/rollup/modules.js b/scripts/rollup/modules.js index 226ea5be5b77c..80c0a6c2cddbe 100644 --- a/scripts/rollup/modules.js +++ b/scripts/rollup/modules.js @@ -10,6 +10,7 @@ const HAS_NO_SIDE_EFFECTS_ON_IMPORT = false; // const HAS_SIDE_EFFECTS_ON_IMPORT = true; const importSideEffects = Object.freeze({ fs: HAS_NO_SIDE_EFFECTS_ON_IMPORT, + 'fs/promises': HAS_NO_SIDE_EFFECTS_ON_IMPORT, path: HAS_NO_SIDE_EFFECTS_ON_IMPORT, 'prop-types/checkPropTypes': HAS_NO_SIDE_EFFECTS_ON_IMPORT, 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface': HAS_NO_SIDE_EFFECTS_ON_IMPORT, From 57791695b2776a9c7f5c930fbe4ad16deb613261 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 9 Dec 2020 04:08:20 +0000 Subject: [PATCH 3/8] Add fs.lstat --- packages/react-fs/src/ReactFilesystem.js | 34 ++++++++++++++++++++++++ scripts/flow/environment.js | 4 +++ 2 files changed, 38 insertions(+) diff --git a/packages/react-fs/src/ReactFilesystem.js b/packages/react-fs/src/ReactFilesystem.js index 4e743c5e0c693..6add72c98de91 100644 --- a/packages/react-fs/src/ReactFilesystem.js +++ b/packages/react-fs/src/ReactFilesystem.js @@ -124,6 +124,40 @@ export function access(path: string, mode?: number): void { readRecord(record); // No return value. } +function createLstatCache(): Map>> { + return new Map(); +} + +export function lstat(path: string, options?: {bigint?: boolean}): mixed { + checkPathInDev(path); + let bigint = false; + if (options && options.bigint) { + bigint = true; + } + const map = unstable_getCacheForType(createLstatCache); + let lstatCache = map.get(path); + if (!lstatCache) { + lstatCache = []; + map.set(path, lstatCache); + } + let record; + for (let i = 0; i < lstatCache.length; i += 2) { + const cachedBigint: boolean = (lstatCache[i]: any); + if (bigint === cachedBigint) { + const cachedRecord: Record = (lstatCache[i + 1]: any); + record = cachedRecord; + break; + } + } + if (!record) { + const thenable = fs.lstat(path, {bigint}); + record = createRecordFromThenable(thenable); + lstatCache.push(bigint, record); + } + const stat = readRecord(record).value; + return stat; +} + function createReadFileCache(): Map> { return new Map(); } diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index 09dc8bfa9927f..0dc439db23e5e 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -72,6 +72,10 @@ declare function __webpack_require__(id: string): any; declare module 'fs/promises' { declare var access: (path: string, mode?: number) => Promise; + declare var lstat: ( + path: string, + options?: ?{bigint?: boolean}, + ) => Promise; declare var readFile: ( path: string, options?: From 127f5f3ed074ef8488bca98714aaca0fbdeef415 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 9 Dec 2020 04:11:43 +0000 Subject: [PATCH 4/8] Add fs.stat --- packages/react-fs/src/ReactFilesystem.js | 38 ++++++++++++++++++++++-- scripts/flow/environment.js | 4 +++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/react-fs/src/ReactFilesystem.js b/packages/react-fs/src/ReactFilesystem.js index 6add72c98de91..f0fa8e26c8ed4 100644 --- a/packages/react-fs/src/ReactFilesystem.js +++ b/packages/react-fs/src/ReactFilesystem.js @@ -154,8 +154,8 @@ export function lstat(path: string, options?: {bigint?: boolean}): mixed { record = createRecordFromThenable(thenable); lstatCache.push(bigint, record); } - const stat = readRecord(record).value; - return stat; + const stats = readRecord(record).value; + return stats; } function createReadFileCache(): Map> { @@ -214,3 +214,37 @@ export function readFile( textCache.push(encoding, text); return text; } + +function createStatCache(): Map>> { + return new Map(); +} + +export function stat(path: string, options?: {bigint?: boolean}): mixed { + checkPathInDev(path); + let bigint = false; + if (options && options.bigint) { + bigint = true; + } + const map = unstable_getCacheForType(createStatCache); + let statCache = map.get(path); + if (!statCache) { + statCache = []; + map.set(path, statCache); + } + let record; + for (let i = 0; i < statCache.length; i += 2) { + const cachedBigint: boolean = (statCache[i]: any); + if (bigint === cachedBigint) { + const cachedRecord: Record = (statCache[i + 1]: any); + record = cachedRecord; + break; + } + } + if (!record) { + const thenable = fs.stat(path, {bigint}); + record = createRecordFromThenable(thenable); + statCache.push(bigint, record); + } + const stats = readRecord(record).value; + return stats; +} diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index 0dc439db23e5e..f9e920996ac38 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -84,6 +84,10 @@ declare module 'fs/promises' { encoding?: ?string, }, ) => Promise; + declare var stat: ( + path: string, + options?: ?{bigint?: boolean}, + ) => Promise; } declare module 'pg' { declare var Pool: ( From 9fc45d158572829c316790307043fe295135ab47 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 9 Dec 2020 04:25:47 +0000 Subject: [PATCH 5/8] Add fs.readdir --- packages/react-fs/src/ReactFilesystem.js | 49 ++++++++++++++++++++++++ scripts/flow/environment.js | 9 +++++ 2 files changed, 58 insertions(+) diff --git a/packages/react-fs/src/ReactFilesystem.js b/packages/react-fs/src/ReactFilesystem.js index f0fa8e26c8ed4..15f5cdd39da7e 100644 --- a/packages/react-fs/src/ReactFilesystem.js +++ b/packages/react-fs/src/ReactFilesystem.js @@ -158,6 +158,55 @@ export function lstat(path: string, options?: {bigint?: boolean}): mixed { return stats; } +function createReaddirCache(): Map< + string, + Array>, +> { + return new Map(); +} + +export function readdir( + path: string, + options?: string | {encoding?: string, withFileTypes?: boolean}, +): mixed { + checkPathInDev(path); + let encoding = 'utf8'; + let withFileTypes = false; + if (typeof options === 'string') { + encoding = options; + } else if (options != null) { + if (options.encoding) { + encoding = options.encoding; + } + if (options.withFileTypes) { + withFileTypes = true; + } + } + const map = unstable_getCacheForType(createReaddirCache); + let readdirCache = map.get(path); + if (!readdirCache) { + readdirCache = []; + map.set(path, readdirCache); + } + let record; + for (let i = 0; i < readdirCache.length; i += 3) { + const cachedEncoding: string = (readdirCache[i]: any); + const cachedWithFileTypes: boolean = (readdirCache[i + 1]: any); + if (encoding === cachedEncoding && withFileTypes === cachedWithFileTypes) { + const cachedRecord: Record = (readdirCache[i + 2]: any); + record = cachedRecord; + break; + } + } + if (!record) { + const thenable = fs.readdir(path, {encoding, withFileTypes}); + record = createRecordFromThenable(thenable); + readdirCache.push(encoding, withFileTypes, record); + } + const files = readRecord(record).value; + return files; +} + function createReadFileCache(): Map> { return new Map(); } diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index f9e920996ac38..a53ac34193ca5 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -76,6 +76,15 @@ declare module 'fs/promises' { path: string, options?: ?{bigint?: boolean}, ) => Promise; + declare var readdir: ( + path: string, + options?: + | ?string + | { + encoding?: ?string, + withFileTypes?: ?boolean, + }, + ) => Promise; declare var readFile: ( path: string, options?: From cc03a28bba360732f835e5512c4550a5e0c9b76b Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 9 Dec 2020 04:35:00 +0000 Subject: [PATCH 6/8] Add fs.readlink --- packages/react-fs/src/ReactFilesystem.js | 41 ++++++++++++++++++++++++ scripts/flow/environment.js | 8 +++++ 2 files changed, 49 insertions(+) diff --git a/packages/react-fs/src/ReactFilesystem.js b/packages/react-fs/src/ReactFilesystem.js index 15f5cdd39da7e..50975755f2874 100644 --- a/packages/react-fs/src/ReactFilesystem.js +++ b/packages/react-fs/src/ReactFilesystem.js @@ -264,6 +264,47 @@ export function readFile( return text; } +function createReadlinkCache(): Map>> { + return new Map(); +} + +export function readlink( + path: string, + options?: string | {encoding?: string}, +): mixed { + checkPathInDev(path); + let encoding = 'utf8'; + if (typeof options === 'string') { + encoding = options; + } else if (options != null) { + if (options.encoding) { + encoding = options.encoding; + } + } + const map = unstable_getCacheForType(createReadlinkCache); + let readlinkCache = map.get(path); + if (!readlinkCache) { + readlinkCache = []; + map.set(path, readlinkCache); + } + let record; + for (let i = 0; i < readlinkCache.length; i += 2) { + const cachedEncoding: string = (readlinkCache[i]: any); + if (encoding === cachedEncoding) { + const cachedRecord: Record = (readlinkCache[i + 1]: any); + record = cachedRecord; + break; + } + } + if (!record) { + const thenable = fs.readlink(path, {encoding}); + record = createRecordFromThenable(thenable); + readlinkCache.push(encoding, record); + } + const linkString = readRecord(record).value; + return linkString; +} + function createStatCache(): Map>> { return new Map(); } diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index a53ac34193ca5..0f3486f01a046 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -93,6 +93,14 @@ declare module 'fs/promises' { encoding?: ?string, }, ) => Promise; + declare var readlink: ( + path: string, + options?: + | ?string + | { + encoding?: ?string, + }, + ) => Promise; declare var stat: ( path: string, options?: ?{bigint?: boolean}, From 60d0b08d93f153dded59bf9c7361218fb5dd31fa Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 9 Dec 2020 04:38:37 +0000 Subject: [PATCH 7/8] Add fs.realpath --- packages/react-fs/src/ReactFilesystem.js | 41 ++++++++++++++++++++++++ scripts/flow/environment.js | 8 +++++ 2 files changed, 49 insertions(+) diff --git a/packages/react-fs/src/ReactFilesystem.js b/packages/react-fs/src/ReactFilesystem.js index 50975755f2874..edf82678bc779 100644 --- a/packages/react-fs/src/ReactFilesystem.js +++ b/packages/react-fs/src/ReactFilesystem.js @@ -305,6 +305,47 @@ export function readlink( return linkString; } +function createRealpathCache(): Map>> { + return new Map(); +} + +export function realpath( + path: string, + options?: string | {encoding?: string}, +): mixed { + checkPathInDev(path); + let encoding = 'utf8'; + if (typeof options === 'string') { + encoding = options; + } else if (options != null) { + if (options.encoding) { + encoding = options.encoding; + } + } + const map = unstable_getCacheForType(createRealpathCache); + let realpathCache = map.get(path); + if (!realpathCache) { + realpathCache = []; + map.set(path, realpathCache); + } + let record; + for (let i = 0; i < realpathCache.length; i += 2) { + const cachedEncoding: string = (realpathCache[i]: any); + if (encoding === cachedEncoding) { + const cachedRecord: Record = (realpathCache[i + 1]: any); + record = cachedRecord; + break; + } + } + if (!record) { + const thenable = fs.realpath(path, {encoding}); + record = createRecordFromThenable(thenable); + realpathCache.push(encoding, record); + } + const resolvedPath = readRecord(record).value; + return resolvedPath; +} + function createStatCache(): Map>> { return new Map(); } diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index 0f3486f01a046..86b19777cd31f 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -101,6 +101,14 @@ declare module 'fs/promises' { encoding?: ?string, }, ) => Promise; + declare var realpath: ( + path: string, + options?: + | ?string + | { + encoding?: ?string, + }, + ) => Promise; declare var stat: ( path: string, options?: ?{bigint?: boolean}, From 9becd74fbb4ac1d37113da10b5fd7e7ce964b037 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 9 Dec 2020 04:52:15 +0000 Subject: [PATCH 8/8] Rename functions to disambiguate two caches --- packages/react-fs/src/ReactFilesystem.js | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/react-fs/src/ReactFilesystem.js b/packages/react-fs/src/ReactFilesystem.js index edf82678bc779..e505005e21db2 100644 --- a/packages/react-fs/src/ReactFilesystem.js +++ b/packages/react-fs/src/ReactFilesystem.js @@ -92,7 +92,7 @@ function checkPathInDev(path: string) { } } -function createAccessCache(): Map>> { +function createAccessMap(): Map>> { return new Map(); } @@ -101,7 +101,7 @@ export function access(path: string, mode?: number): void { if (mode == null) { mode = 0; // fs.constants.F_OK } - const map = unstable_getCacheForType(createAccessCache); + const map = unstable_getCacheForType(createAccessMap); let accessCache = map.get(path); if (!accessCache) { accessCache = []; @@ -124,7 +124,7 @@ export function access(path: string, mode?: number): void { readRecord(record); // No return value. } -function createLstatCache(): Map>> { +function createLstatMap(): Map>> { return new Map(); } @@ -134,7 +134,7 @@ export function lstat(path: string, options?: {bigint?: boolean}): mixed { if (options && options.bigint) { bigint = true; } - const map = unstable_getCacheForType(createLstatCache); + const map = unstable_getCacheForType(createLstatMap); let lstatCache = map.get(path); if (!lstatCache) { lstatCache = []; @@ -158,7 +158,7 @@ export function lstat(path: string, options?: {bigint?: boolean}): mixed { return stats; } -function createReaddirCache(): Map< +function createReaddirMap(): Map< string, Array>, > { @@ -182,7 +182,7 @@ export function readdir( withFileTypes = true; } } - const map = unstable_getCacheForType(createReaddirCache); + const map = unstable_getCacheForType(createReaddirMap); let readdirCache = map.get(path); if (!readdirCache) { readdirCache = []; @@ -207,7 +207,7 @@ export function readdir( return files; } -function createReadFileCache(): Map> { +function createReadFileMap(): Map> { return new Map(); } @@ -223,7 +223,7 @@ export function readFile( }, ): string | Buffer { checkPathInDev(path); - const map = unstable_getCacheForType(createReadFileCache); + const map = unstable_getCacheForType(createReadFileMap); let record = map.get(path); if (!record) { const thenable = fs.readFile(path); @@ -264,7 +264,7 @@ export function readFile( return text; } -function createReadlinkCache(): Map>> { +function createReadlinkMap(): Map>> { return new Map(); } @@ -281,7 +281,7 @@ export function readlink( encoding = options.encoding; } } - const map = unstable_getCacheForType(createReadlinkCache); + const map = unstable_getCacheForType(createReadlinkMap); let readlinkCache = map.get(path); if (!readlinkCache) { readlinkCache = []; @@ -305,7 +305,7 @@ export function readlink( return linkString; } -function createRealpathCache(): Map>> { +function createRealpathMap(): Map>> { return new Map(); } @@ -322,7 +322,7 @@ export function realpath( encoding = options.encoding; } } - const map = unstable_getCacheForType(createRealpathCache); + const map = unstable_getCacheForType(createRealpathMap); let realpathCache = map.get(path); if (!realpathCache) { realpathCache = []; @@ -346,7 +346,7 @@ export function realpath( return resolvedPath; } -function createStatCache(): Map>> { +function createStatMap(): Map>> { return new Map(); } @@ -356,7 +356,7 @@ export function stat(path: string, options?: {bigint?: boolean}): mixed { if (options && options.bigint) { bigint = true; } - const map = unstable_getCacheForType(createStatCache); + const map = unstable_getCacheForType(createStatMap); let statCache = map.get(path); if (!statCache) { statCache = [];