Skip to content

Commit

Permalink
Merge pull request #1697 from hameleonka/1536-io-refactoring
Browse files Browse the repository at this point in the history
Async/await refactoring for files in io folder except directory.js
  • Loading branch information
EnTeQuAk authored Jan 3, 2018
2 parents e10b555 + 70201ca commit 3e8e274
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 168 deletions.
49 changes: 20 additions & 29 deletions src/io/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,50 +45,41 @@ export class IOBase {
}
}

getFilesByExt(...extensions) {
async getFilesByExt(...extensions) {
for (let i = 0; i < extensions.length; i++) {
const ext = extensions[i];
if (ext.indexOf('.') !== 0) {
// We use Promise.reject as we're not inside a `then()` or a
// Promise constructor callback.
// If we throw here it won't be caught.
return Promise.reject(new Error("File extension must start with '.'"));
throw new Error("File extension must start with '.'");
}
}

return this.getFiles()
.then((filesObject) => {
const files = [];
const filesObject = await this.getFiles();
const files = [];

Object.keys(filesObject).forEach((filename) => {
extensions.forEach((ext) => {
if (filename.endsWith(ext)) {
files.push(filename);
}
});
});

return files;
Object.keys(filesObject).forEach((filename) => {
extensions.forEach((ext) => {
if (filename.endsWith(ext)) {
files.push(filename);
}
});
});

return files;
}

getFiles() {
return Promise.reject(
new Error('getFiles is not implemented'));
async getFiles() {
throw new Error('getFiles is not implemented');
}

getFileAsStream() {
return Promise.reject(
new Error('getFileAsStream is not implemented'));
async getFileAsStream() {
throw new Error('getFileAsStream is not implemented');
}

getFileAsString() {
return Promise.reject(
new Error('getFileAsString is not implemented'));
async getFileAsString() {
throw new Error('getFileAsString is not implemented');
}

getChunkAsBuffer() {
return Promise.reject(
new Error('getChunkAsBuffer is not implemented'));
async getChunkAsBuffer() {
throw new Error('getChunkAsBuffer is not implemented');
}
}
54 changes: 26 additions & 28 deletions src/io/crx.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,37 +50,35 @@ export class Crx extends Xpi {
});
}

getFiles(_onEventsSubscribed) {
return new Promise((resolve, reject) => {
// If we have already processed the file and have data
// on this instance return that.
if (Object.keys(this.files).length) {
return resolve(this.files);
}
async getFiles(_onEventsSubscribed) {
// If we have already processed the file and have data
// on this instance return that.
if (Object.keys(this.files).length) {
return this.files;
}

return this.open()
.then((zipfile) => {
zipfile.on('entry', (entry) => {
this.handleEntry(entry, reject);
});
const zipfile = await this.open();

// We use the 'end' event here because we're reading the CRX in
// from a buffer (because we have to unpack the header info from it
// first). The 'close' event is never fired when using yauzl's
// `fromBuffer()` method.
zipfile.on('end', () => {
resolve(this.files);
});
// We use the 'end' event here because we're reading the CRX in
// from a buffer (because we have to unpack the header info from it
// first). The 'close' event is never fired when using yauzl's
// `fromBuffer()` method.
return new Promise((resolve, reject) => {
zipfile.on('entry', (entry) => {
this.handleEntry(entry, reject);
});

if (_onEventsSubscribed) {
// Run optional callback when we know the event handlers
// have been inited. Useful for testing.
if (typeof _onEventsSubscribed === 'function') {
_onEventsSubscribed();
}
}
})
.catch(reject);
zipfile.on('end', () => {
resolve(this.files);
});

if (_onEventsSubscribed) {
// Run optional callback when we know the event handlers
// have been inited. Useful for testing.
if (typeof _onEventsSubscribed === 'function') {
Promise.resolve().then(() => _onEventsSubscribed());
}
}
});
}
}
45 changes: 19 additions & 26 deletions src/io/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,25 @@ export function walkPromise(curPath, { shouldIncludePath = () => true } = {}) {
// so all file paths (the result keys) can
// be relative to the starting point.
const basePath = curPath;
return (async function walk(_curPath) {
const stat = await lstatPromise(_curPath);
const relPath = path.relative(basePath, _curPath);

return (function walk(_curPath) {
return lstatPromise(_curPath)
// eslint-disable-next-line consistent-return
.then((stat) => {
const relPath = path.relative(basePath, _curPath);
if (!shouldIncludePath(relPath, stat.isDirectory())) {
log.debug(`Skipping file path: ${relPath}`);
return result;
} else if (stat.isFile()) {
const { size } = stat;
result[relPath] = { size };
} else if (stat.isDirectory()) {
return readdirPromise(_curPath)
.then((files) => {
// Map the list of files and make a list of readdir
// promises to pass to Promise.all so we can recursively
// get the data on all the files in the directory.
return Promise.all(files.map((fileName) => {
return walk(path.join(_curPath, fileName));
}));
})
.then(() => {
return result;
});
}
});
if (!shouldIncludePath(relPath, stat.isDirectory())) {
log.debug(`Skipping file path: ${relPath}`);
} else if (stat.isFile()) {
const { size } = stat;
result[relPath] = { size };
} else if (stat.isDirectory()) {
const files = await readdirPromise(_curPath);

// Map the list of files and make a list of readdir
// promises to pass to Promise.all so we can recursively
// get the data on all the files in the directory.
await Promise.all(files.map(async (fileName) => {
await walk(path.join(_curPath, fileName));
}));
}
return result;
}(curPath));
}
162 changes: 77 additions & 85 deletions src/io/xpi.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,51 +45,50 @@ export class Xpi extends IOBase {
log.info('Found duplicate file entry: "%s" in package', entry.fileName);
reject(new Error(oneLine`DuplicateZipEntry: Entry
"${entry.fileName}" has already been seen`));
return;
}
this.entries.push(entry.fileName);
this.files[entry.fileName] = entry;
}

getFiles(_onEventsSubscribed) {
async getFiles(_onEventsSubscribed) {
// If we have already processed the file and have data
// on this instance return that.
if (Object.keys(this.files).length) {
const wantedFiles = {};
Object.keys(this.files).forEach((fileName) => {
if (this.shouldScanFile(fileName)) {
wantedFiles[fileName] = this.files[fileName];
} else {
log.debug(`Skipping cached file: ${fileName}`);
}
});
return wantedFiles;
}

const zipfile = await this.open();

return new Promise((resolve, reject) => {
// If we have already processed the file and have data
// on this instance return that.
if (Object.keys(this.files).length) {
const wantedFiles = {};
Object.keys(this.files).forEach((fileName) => {
if (this.shouldScanFile(fileName)) {
wantedFiles[fileName] = this.files[fileName];
} else {
log.debug(`Skipping cached file: ${fileName}`);
}
});
return resolve(wantedFiles);
}
zipfile.on('entry', (entry) => {
this.handleEntry(entry, reject);
});

return this.open()
.then((zipfile) => {
zipfile.on('entry', (entry) => {
this.handleEntry(entry, reject);
});

// When the last entry has been processed
// and the fd is closed resolve the promise.
// Note: we cannot use 'end' here as 'end' is fired
// after the last entry event is emitted and streams
// may still be being read with openReadStream.
zipfile.on('close', () => {
resolve(this.files);
});

if (_onEventsSubscribed) {
// Run optional callback when we know the event handlers
// have been inited. Useful for testing.
if (typeof _onEventsSubscribed === 'function') {
_onEventsSubscribed();
}
}
})
.catch(reject);
// When the last entry has been processed
// and the fd is closed resolve the promise.
// Note: we cannot use 'end' here as 'end' is fired
// after the last entry event is emitted and streams
// may still be being read with openReadStream.
zipfile.on('close', () => {
resolve(this.files);
});

if (_onEventsSubscribed) {
// Run optional callback when we know the event handlers
// have been inited. Useful for testing.
if (typeof _onEventsSubscribed === 'function') {
Promise.resolve().then(() => _onEventsSubscribed());
}
}
});
}

Expand All @@ -103,62 +102,55 @@ export class Xpi extends IOBase {
}
}

getFileAsStream(path) {
async getFileAsStream(path) {
this.checkPath(path);
const zipfile = await this.open();
return new Promise((resolve, reject) => {
this.checkPath(path);
return this.open()
.then((zipfile) => {
zipfile.openReadStream(this.files[path], (err, readStream) => {
if (err) {
return reject(err);
}
return resolve(readStream.pipe(stripBomStream()));
});
})
.catch(reject);
zipfile.openReadStream(this.files[path], (err, readStream) => {
if (err) {
return reject(err);
}
return resolve(readStream.pipe(stripBomStream()));
});
});
}

getFileAsString(path) {
return this.getFileAsStream(path)
.then((fileStream) => {
return new Promise((resolve, reject) => {
let buf = Buffer.from('');
fileStream.on('data', (chunk) => {
buf = Buffer.concat([buf, chunk]);
});

// Once the file is assembled, resolve the promise.
fileStream.on('end', () => {
const fileString = buf.toString('utf8');
resolve(fileString);
});

fileStream.on('error', reject);
});
async getFileAsString(path) {
const fileStream = await this.getFileAsStream(path);

return new Promise((resolve, reject) => {
let buf = Buffer.from('');
fileStream.on('data', (chunk) => {
buf = Buffer.concat([buf, chunk]);
});

// Once the file is assembled, resolve the promise.
fileStream.on('end', () => {
const fileString = buf.toString('utf8');
resolve(fileString);
});

fileStream.on('error', reject);
});
}

getChunkAsBuffer(path, chunkLength) {
async getChunkAsBuffer(path, chunkLength) {
this.checkPath(path);
const zipfile = await this.open();
return new Promise((resolve, reject) => {
this.checkPath(path);
return this.open()
.then((zipfile) => {
// eslint-disable-next-line consistent-return
zipfile.openReadStream(this.files[path], (err, readStream) => {
if (err) {
return reject(err);
zipfile.openReadStream(this.files[path], (err, readStream) => {
if (err) {
reject(err);
return;
}
readStream.pipe(
firstChunkStream({ chunkLength },
(_, enc) => {
resolve(enc);
}
readStream.pipe(
firstChunkStream({ chunkLength },
(_, enc) => {
resolve(enc);
}
)
);
});
})
.catch(reject);
)
);
});
});
}
}

0 comments on commit 3e8e274

Please sign in to comment.