RM fastfs.js: @flow

Reviewed By: cpojer

Differential Revision: D4118175

fbshipit-source-id: ad9d1f98bcde0c1be17d36c74c3662e6e55165f8
This commit is contained in:
Jean Lauliac 2016-11-03 04:50:19 -07:00 committed by Facebook Github Bot
parent d730928b0c
commit 4fe80636fb
1 changed files with 60 additions and 11 deletions

View File

@ -5,7 +5,10 @@
* This source code is licensed under the BSD-style license found in the * 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 * 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. * of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/ */
'use strict'; 'use strict';
const fs = require('fs'); const fs = require('fs');
@ -15,8 +18,34 @@ const {EventEmitter} = require('events');
const NOT_FOUND_IN_ROOTS = 'NotFoundInRootsError'; const NOT_FOUND_IN_ROOTS = 'NotFoundInRootsError';
interface Activity {
startEvent(title: string, m: mixed, opts: mixed): mixed,
endEvent(activity: mixed): void,
}
interface FileWatcher {
on(event: 'all', handler: (type: string, filePath: string, rootPath: string, fstat: fs.Stats) => void): void,
}
class Fastfs extends EventEmitter { class Fastfs extends EventEmitter {
constructor(name, roots, fileWatcher, files, {ignore, activity}) {
_name: string;
_fileWatcher: FileWatcher;
_ignore: (filePath: string) => boolean;
_roots: Array<File>;
_fastPaths: {[filePath: string]: File};
_activity: mixed;
constructor(
name: string,
roots: Array<string>,
fileWatcher: FileWatcher,
files: Array<string>,
{ignore, activity}: {
ignore: (filePath: string) => boolean,
activity: Activity,
},
) {
super(); super();
this._name = name; this._name = name;
this._fileWatcher = fileWatcher; this._fileWatcher = fileWatcher;
@ -68,7 +97,7 @@ class Fastfs extends EventEmitter {
} }
} }
stat(filePath) { stat(filePath: string) {
return Promise.resolve().then(() => this._getFile(filePath).stat()); return Promise.resolve().then(() => this._getFile(filePath).stat());
} }
@ -77,7 +106,10 @@ class Fastfs extends EventEmitter {
.filter(filePath => !this._fastPaths[filePath].isDir); .filter(filePath => !this._fastPaths[filePath].isDir);
} }
findFilesByExts(exts, { ignore } = {}) { findFilesByExts(
exts: Array<string>,
{ignore}: {ignore: (filePath: string) => boolean} = {},
) {
return this.getAllFiles() return this.getAllFiles()
.filter(filePath => ( .filter(filePath => (
exts.indexOf(path.extname(filePath).substr(1)) !== -1 && exts.indexOf(path.extname(filePath).substr(1)) !== -1 &&
@ -85,11 +117,11 @@ class Fastfs extends EventEmitter {
)); ));
} }
matchFilesByPattern(pattern) { matchFilesByPattern(pattern: RegExp) {
return this.getAllFiles().filter(file => file.match(pattern)); return this.getAllFiles().filter(file => file.match(pattern));
} }
readFile(filePath) { readFile(filePath: string) {
const file = this._getFile(filePath); const file = this._getFile(filePath);
if (!file) { if (!file) {
throw new Error(`Unable to find file with path: ${filePath}`); throw new Error(`Unable to find file with path: ${filePath}`);
@ -97,10 +129,11 @@ class Fastfs extends EventEmitter {
return file.read(); return file.read();
} }
closest(filePath, name) { closest(filePath: string, name: string) {
for (let file = this._getFile(filePath).parent; for (let file = this._getFile(filePath).parent;
file; file;
file = file.parent) { file = file.parent) {
/* $FlowFixMe: will crash if not `isDir`, see constructor */
if (file.children[name]) { if (file.children[name]) {
return file.children[name].path; return file.children[name].path;
} }
@ -108,7 +141,7 @@ class Fastfs extends EventEmitter {
return null; return null;
} }
fileExists(filePath) { fileExists(filePath: string) {
let file; let file;
try { try {
file = this._getFile(filePath); file = this._getFile(filePath);
@ -122,7 +155,7 @@ class Fastfs extends EventEmitter {
return file && !file.isDir; return file && !file.isDir;
} }
dirExists(filePath) { dirExists(filePath: string) {
let file; let file;
try { try {
file = this._getFile(filePath); file = this._getFile(filePath);
@ -136,12 +169,13 @@ class Fastfs extends EventEmitter {
return file && file.isDir; return file && file.isDir;
} }
matches(dir, pattern) { matches(dir: string, pattern: RegExp) {
const dirFile = this._getFile(dir); const dirFile = this._getFile(dir);
if (!dirFile.isDir) { if (!dirFile.isDir) {
throw new Error(`Expected file ${dirFile.path} to be a directory`); throw new Error(`Expected file ${dirFile.path} to be a directory`);
} }
/* $FlowFixMe: will crash if not `isDir`, see constructor */
return Object.keys(dirFile.children) return Object.keys(dirFile.children)
.filter(name => name.match(pattern)) .filter(name => name.match(pattern))
.map(name => path.join(dirFile.path, name)); .map(name => path.join(dirFile.path, name));
@ -161,6 +195,7 @@ class Fastfs extends EventEmitter {
const root = this._getRoot(filePath); const root = this._getRoot(filePath);
if (!root) { if (!root) {
const error = new Error(`File ${filePath} not found in any of the roots`); const error = new Error(`File ${filePath} not found in any of the roots`);
/* $FlowFixMe: Monkey-patching Error. */
error.type = NOT_FOUND_IN_ROOTS; error.type = NOT_FOUND_IN_ROOTS;
throw error; throw error;
} }
@ -210,7 +245,16 @@ class Fastfs extends EventEmitter {
} }
class File { class File {
constructor(filePath, isDir) {
path: string;
isDir: boolean;
children: ?{[filePath: string]: File};
parent: ?File;
_read: ?Promise<string>;
_stat: ?Promise<fs.Stats>;
constructor(filePath: string, isDir: boolean) {
this.path = filePath; this.path = filePath;
this.isDir = isDir; this.isDir = isDir;
this.children = this.isDir ? Object.create(null) : null; this.children = this.isDir ? Object.create(null) : null;
@ -243,16 +287,19 @@ class File {
return this._stat; return this._stat;
} }
addChild(file, fileMap) { addChild(file: File, fileMap: {[filePath: string]: File}) {
const parts = file.path.substr(this.path.length + 1).split(path.sep); const parts = file.path.substr(this.path.length + 1).split(path.sep);
if (parts.length === 1) { if (parts.length === 1) {
/* $FlowFixMe: will crash if not `isDir`, see constructor */
this.children[parts[0]] = file; this.children[parts[0]] = file;
file.parent = this; file.parent = this;
/* $FlowFixMe: will crash if not `isDir`, see constructor */
} else if (this.children[parts[0]]) { } else if (this.children[parts[0]]) {
this.children[parts[0]].addChild(file, fileMap); this.children[parts[0]].addChild(file, fileMap);
} else { } else {
const dir = new File(this.path + path.sep + parts[0], true); const dir = new File(this.path + path.sep + parts[0], true);
dir.parent = this; dir.parent = this;
/* $FlowFixMe: will crash if not `isDir`, see constructor */
this.children[parts[0]] = dir; this.children[parts[0]] = dir;
fileMap[dir.path] = dir; fileMap[dir.path] = dir;
dir.addChild(file, fileMap); dir.addChild(file, fileMap);
@ -275,6 +322,7 @@ class File {
return null; return null;
} }
/* $FlowFixMe: will crash if not `isDir`, see constructor */
file = file.children[fileName]; file = file.children[fileName];
} }
@ -290,6 +338,7 @@ class File {
throw new Error(`No parent to delete ${this.path} from`); throw new Error(`No parent to delete ${this.path} from`);
} }
/* $FlowFixMe: will crash if parent is not `isDir`, see constructor */
delete this.parent.children[path.basename(this.path)]; delete this.parent.children[path.basename(this.path)];
} }
} }