Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async/await refactoring for files in io folder except directory.js #1697

Merged
merged 3 commits into from
Jan 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
)
);
});
});
}
}