mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
[Flight] Add read-only fs methods (#20412)
* 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. * Add fs.access * Add fs.lstat * Add fs.stat * Add fs.readdir * Add fs.readlink * Add fs.realpath * Rename functions to disambiguate two caches
This commit is contained in:
+243
-10
@@ -20,19 +20,19 @@ const Rejected = 2;
|
||||
type PendingRecord = {|
|
||||
status: 0,
|
||||
value: Wakeable,
|
||||
cache: Array<mixed>,
|
||||
cache: null,
|
||||
|};
|
||||
|
||||
type ResolvedRecord<T> = {|
|
||||
status: 1,
|
||||
value: T,
|
||||
cache: Array<mixed>,
|
||||
cache: null | Array<mixed>,
|
||||
|};
|
||||
|
||||
type RejectedRecord = {|
|
||||
status: 2,
|
||||
value: mixed,
|
||||
cache: Array<mixed>,
|
||||
cache: null,
|
||||
|};
|
||||
|
||||
type Record<T> = PendingRecord | ResolvedRecord<T> | RejectedRecord;
|
||||
@@ -41,7 +41,7 @@ function createRecordFromThenable<T>(thenable: Thenable<T>): Record<T> {
|
||||
const record: Record<T> = {
|
||||
status: Pending,
|
||||
value: thenable,
|
||||
cache: [],
|
||||
cache: null,
|
||||
};
|
||||
thenable.then(
|
||||
value => {
|
||||
@@ -62,9 +62,10 @@ function createRecordFromThenable<T>(thenable: Thenable<T>): Record<T> {
|
||||
return record;
|
||||
}
|
||||
|
||||
function readRecordValue<T>(record: Record<T>): T {
|
||||
function readRecord<T>(record: Record<T>): ResolvedRecord<T> {
|
||||
if (record.status === Resolved) {
|
||||
return record.value;
|
||||
// This is just a type refinement.
|
||||
return record;
|
||||
} else {
|
||||
throw record.value;
|
||||
}
|
||||
@@ -91,7 +92,122 @@ function checkPathInDev(path: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function createReadFileCache(): Map<string, Record<Buffer>> {
|
||||
function createAccessMap(): Map<string, Array<number | Record<void>>> {
|
||||
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(createAccessMap);
|
||||
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<void> = (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 createLstatMap(): Map<string, Array<boolean | Record<mixed>>> {
|
||||
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(createLstatMap);
|
||||
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<void> = (lstatCache[i + 1]: any);
|
||||
record = cachedRecord;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!record) {
|
||||
const thenable = fs.lstat(path, {bigint});
|
||||
record = createRecordFromThenable(thenable);
|
||||
lstatCache.push(bigint, record);
|
||||
}
|
||||
const stats = readRecord(record).value;
|
||||
return stats;
|
||||
}
|
||||
|
||||
function createReaddirMap(): Map<
|
||||
string,
|
||||
Array<string | boolean | Record<mixed>>,
|
||||
> {
|
||||
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(createReaddirMap);
|
||||
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<void> = (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 createReadFileMap(): Map<string, Record<Buffer>> {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
@@ -106,15 +222,16 @@ export function readFile(
|
||||
signal?: mixed, // We'll have our own signal
|
||||
},
|
||||
): string | Buffer {
|
||||
const map = unstable_getCacheForType(createReadFileCache);
|
||||
checkPathInDev(path);
|
||||
const map = unstable_getCacheForType(createReadFileMap);
|
||||
let record = map.get(path);
|
||||
if (!record) {
|
||||
const thenable = fs.readFile(path);
|
||||
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 +253,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);
|
||||
@@ -146,3 +263,119 @@ export function readFile(
|
||||
textCache.push(encoding, text);
|
||||
return text;
|
||||
}
|
||||
|
||||
function createReadlinkMap(): Map<string, Array<string | Record<mixed>>> {
|
||||
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(createReadlinkMap);
|
||||
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<void> = (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 createRealpathMap(): Map<string, Array<string | Record<mixed>>> {
|
||||
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(createRealpathMap);
|
||||
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<void> = (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 createStatMap(): Map<string, Array<boolean | Record<mixed>>> {
|
||||
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(createStatMap);
|
||||
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<void> = (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;
|
||||
}
|
||||
|
||||
@@ -71,6 +71,20 @@ declare function __webpack_chunk_load__(id: string): Promise<mixed>;
|
||||
declare function __webpack_require__(id: string): any;
|
||||
|
||||
declare module 'fs/promises' {
|
||||
declare var access: (path: string, mode?: number) => Promise<void>;
|
||||
declare var lstat: (
|
||||
path: string,
|
||||
options?: ?{bigint?: boolean},
|
||||
) => Promise<mixed>;
|
||||
declare var readdir: (
|
||||
path: string,
|
||||
options?:
|
||||
| ?string
|
||||
| {
|
||||
encoding?: ?string,
|
||||
withFileTypes?: ?boolean,
|
||||
},
|
||||
) => Promise<Buffer>;
|
||||
declare var readFile: (
|
||||
path: string,
|
||||
options?:
|
||||
@@ -79,6 +93,26 @@ declare module 'fs/promises' {
|
||||
encoding?: ?string,
|
||||
},
|
||||
) => Promise<Buffer>;
|
||||
declare var readlink: (
|
||||
path: string,
|
||||
options?:
|
||||
| ?string
|
||||
| {
|
||||
encoding?: ?string,
|
||||
},
|
||||
) => Promise<mixed>;
|
||||
declare var realpath: (
|
||||
path: string,
|
||||
options?:
|
||||
| ?string
|
||||
| {
|
||||
encoding?: ?string,
|
||||
},
|
||||
) => Promise<mixed>;
|
||||
declare var stat: (
|
||||
path: string,
|
||||
options?: ?{bigint?: boolean},
|
||||
) => Promise<mixed>;
|
||||
}
|
||||
declare module 'pg' {
|
||||
declare var Pool: (
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user