mirror of https://github.com/status-im/metro.git
Add partial reading of files to fastfs
Summary: public Adds the ability to read files partially with `readWhile` to `Fastfs` This feature will be used by for building the haste map, where we only need to read the doc block (if present) to extract the provided module name. Reviewed By: martinbigio Differential Revision: D2878093 fb-gh-sync-id: 219cf6d5962b89eeb42c728e176bf9fcf6a67e9c
This commit is contained in:
parent
df48cd1163
commit
fb09783e81
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
* An arbitrary module header
|
||||
* @providesModule
|
||||
*/
|
||||
|
||||
const some: string = 'arbitrary code';
|
||||
const containing: string = 'ūñïčødę';
|
||||
|
||||
/**
|
||||
* An arbitrary class that extends some thing
|
||||
* It exposes a random number, which may be reset at the callers discretion
|
||||
*/
|
||||
class Arbitrary extends Something {
|
||||
constructor() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the random number
|
||||
* @returns number
|
||||
*/
|
||||
get random(): number {
|
||||
return this._random;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-creates the internal random number
|
||||
* @returns void
|
||||
*/
|
||||
reset(): void {
|
||||
this._random = Math.random();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
jest.autoMockOff();
|
||||
|
||||
const Fastfs = require('../fastfs');
|
||||
|
||||
const {EventEmitter} = require('events');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const fileName = path.resolve(__dirname, 'fastfs-data');
|
||||
const contents = fs.readFileSync(fileName, 'utf-8');
|
||||
|
||||
describe('fastfs:', function() {
|
||||
let fastfs;
|
||||
const crawling = Promise.resolve([fileName]);
|
||||
const roots = [__dirname];
|
||||
const watcher = new EventEmitter();
|
||||
|
||||
beforeEach(function(done) {
|
||||
fastfs = new Fastfs('arbitrary', roots, watcher, {crawling});
|
||||
fastfs.build().then(done);
|
||||
});
|
||||
|
||||
describe('partial reading', () => {
|
||||
// these are integrated tests that read real files from disk
|
||||
|
||||
pit('reads a file while a predicate returns true', function() {
|
||||
return fastfs.readWhile(fileName, () => true).then(readContent =>
|
||||
expect(readContent).toEqual(contents)
|
||||
);
|
||||
});
|
||||
|
||||
pit('invokes the predicate with the new chunk, the invocation index, and the result collected so far', () => {
|
||||
const predicate = jest.genMockFn().mockReturnValue(true);
|
||||
return fastfs.readWhile(fileName, predicate).then(() => {
|
||||
let aggregated = '';
|
||||
const {calls} = predicate.mock;
|
||||
expect(calls).not.toEqual([]);
|
||||
|
||||
calls.forEach((call, i) => {
|
||||
const [chunk] = call;
|
||||
aggregated += chunk;
|
||||
expect(chunk).not.toBe('');
|
||||
expect(call).toEqual([chunk, i, aggregated]);
|
||||
});
|
||||
|
||||
expect(aggregated).toEqual(contents);
|
||||
});
|
||||
});
|
||||
|
||||
pit('stops reading when the predicate returns false', () => {
|
||||
const predicate = jest.genMockFn().mockImpl((_, i) => i !== 0);
|
||||
return fastfs.readWhile(fileName, predicate).then((readContent) => {
|
||||
const {calls} = predicate.mock;
|
||||
expect(calls.length).toBe(1);
|
||||
expect(readContent).toBe(calls[0][2]);
|
||||
});
|
||||
});
|
||||
|
||||
pit('after reading the whole file with `readWhile`, `read()` still works', () => {
|
||||
// this test allows to reuse the results of `readWhile` for `readFile`
|
||||
return fastfs.readWhile(fileName, () => true).then(() => {
|
||||
fastfs.readFile(fileName).then(readContent =>
|
||||
expect(readContent).toEqual(contents)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
pit('after reading parts of the file with `readWhile`, `read()` still works', () => {
|
||||
return fastfs.readWhile(fileName, () => false).then(() => {
|
||||
fastfs.readFile(fileName).then(readContent =>
|
||||
expect(readContent).toEqual(contents)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -14,6 +14,7 @@ const {EventEmitter} = require('events');
|
|||
const fs = require('graceful-fs');
|
||||
const path = require('path');
|
||||
|
||||
const open = Promise.denodeify(fs.open);
|
||||
const readFile = Promise.denodeify(fs.readFile);
|
||||
const stat = Promise.denodeify(fs.stat);
|
||||
|
||||
|
@ -114,6 +115,14 @@ class Fastfs extends EventEmitter {
|
|||
return file.read();
|
||||
}
|
||||
|
||||
readWhile(filePath, predicate) {
|
||||
const file = this._getFile(filePath);
|
||||
if (!file) {
|
||||
throw new Error(`Unable to find file with path: ${filePath}`);
|
||||
}
|
||||
return file.readWhile(predicate);
|
||||
}
|
||||
|
||||
closest(filePath, name) {
|
||||
for (let file = this._getFile(filePath).parent;
|
||||
file;
|
||||
|
@ -241,6 +250,44 @@ class File {
|
|||
return this._read;
|
||||
}
|
||||
|
||||
readWhile(predicate) {
|
||||
const CHUNK_SIZE = 512;
|
||||
let result = '';
|
||||
|
||||
return open(this.path, 'r').then(fd => {
|
||||
/* global Buffer: true */
|
||||
const buffer = new Buffer(CHUNK_SIZE);
|
||||
const p = new Promise((resolve, reject) => {
|
||||
let counter = 0;
|
||||
const callback = (error, bytesRead) => {
|
||||
if (error) {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
|
||||
const chunk = buffer.toString('utf8', 0, bytesRead);
|
||||
result += chunk;
|
||||
if (bytesRead > 0 && predicate(chunk, counter++, result)) {
|
||||
readChunk(fd, buffer, callback);
|
||||
} else {
|
||||
if (bytesRead === 0 && !this._read) { // reached EOF
|
||||
this._read = Promise.resolve(result);
|
||||
}
|
||||
resolve(result);
|
||||
}
|
||||
};
|
||||
readChunk(fd, buffer, callback);
|
||||
});
|
||||
|
||||
p.catch(() => fs.close(fd));
|
||||
return p;
|
||||
});
|
||||
|
||||
function readChunk(fd, buffer, callback) {
|
||||
fs.read(fd, buffer, 0, CHUNK_SIZE, null, callback);
|
||||
}
|
||||
}
|
||||
|
||||
stat() {
|
||||
if (!this._stat) {
|
||||
this._stat = stat(this.path);
|
||||
|
|
Loading…
Reference in New Issue