Transformed the whole generate() execution to a Promise-driven flow

This commit is contained in:
alexandercerutti
2018-08-07 16:23:42 +02:00
parent 551dd8a8e0
commit cc23930a2f

163
index.js
View File

@@ -27,26 +27,22 @@ class Pass {
*/ */
generate() { generate() {
let manifest = {};
let archive = archiver("zip"); let archive = archiver("zip");
return new Promise((success, reject) => { return this._parseSettings(this.options)
let _gen = (() => { .then(() => readdir(this.model))
fs.readdir(this.model, (err, files) => { .catch(() => Promise.reject({
if (err) {
return reject({
status: false, status: false,
error: { error: {
message: "Model not found. Provide a valid one to continue." message: `Model ${this.model} not found. Provide a valid one to continue`
} }
}) }))
} .then(files => {
// list without dynamic components like manifest, signature or pass files (will be added later in the flow) and hidden files. // list without dynamic components like manifest, signature or pass files (will be added later in the flow) and hidden files.
let noDynList = removeHidden(files).filter(f => !/(manifest|signature|pass)/i.test(f)); let noDynList = removeHidden(files).filter(f => !/(manifest|signature|pass)/i.test(f));
if (!noDynList.length) { if (!noDynList.length) {
return reject({ return Promise.reject({
status: false, status: false,
error: { error: {
message: "Model provided matched but unitialized. Refer to https://apple.co/2IhJr0Q and documentation to fill the model correctly." message: "Model provided matched but unitialized. Refer to https://apple.co/2IhJr0Q and documentation to fill the model correctly."
@@ -55,54 +51,21 @@ class Pass {
} }
// list without localization files (they will be added later in the flow) // list without localization files (they will be added later in the flow)
let bundleList = noDynList.filter(f => !f.includes(".lproj")); let bundle = noDynList.filter(f => !f.includes(".lproj"));
const L10N = { // Localization folders only
// localization folders only const L10N = noDynList.filter(f => f.includes(".lproj"));
list: noDynList.filter(f => f.includes(".lproj"))
};
/* /*
* I may have (and I rathered) used async.concat to achieve this but it returns an * Defining pass.json patcher and extractor
* array of filenames ordered by folder, without any kind of folder indication. * Extracting it with the other paths
* So, the problem rises when I have to understand which is the first file of a
* folder which is not the first one, as I don't know how many file there are in
* a folder.
*
* Therefore, I generate a function for each localization (L10N) folder inside the
* model. Each function will read the content of the folder and return an array of
* the filenames inside that L10N folder.
*/ */
L10N.extractors = L10N.list.map(f => ((callback) => { let _passExtractor = (() => {
let l10nPath = path.join(this.model, f); return readFile(path.resolve(this.model, "pass.json"))
.then(passStructBuffer => {
fs.readdir(l10nPath, function(err, list) {
if (err) {
return callback(err, null);
}
let filteredFiles = removeHidden(list);
return callback(null, filteredFiles);
});
}));
// === flow definition ===
let _passExtractor = (passCallback => {
fs.readFile(path.resolve(this.model, "pass.json"), {}, (err, passStructBuffer) => {
if (err) {
// Flow should never enter in there since pass.json existence-check is already done above.
return passCallback({
status: false,
error: {
message: `Unable to read pass.json file @ ${this.model}`
}
});
}
if (!this._validateType(passStructBuffer)) { if (!this._validateType(passStructBuffer)) {
return passCallback({ return Promise.reject({
status: false, status: false,
error: { error: {
message: `Unable to validate pass type or pass file is not a valid buffer. Check the syntax of your pass.json file or refer to https://apple.co/2Nvshvn to use a valid type.` message: `Unable to validate pass type or pass file is not a valid buffer. Check the syntax of your pass.json file or refer to https://apple.co/2Nvshvn to use a valid type.`
@@ -110,55 +73,50 @@ class Pass {
}); });
} }
try { bundle.push("pass.json");
let patchedPass = this._patch(this._filterOptions(this.overrides), passStructBuffer);
manifest["pass.json"] = forge.md.sha1.create().update(patchedPass.toString("binary")).digest().toHex(); return this._patch(this._filterOptions(this.overrides), passStructBuffer);
archive.append(patchedPass, { name: "pass.json" }); })
.catch(err => {
return passCallback(); console.log(err);
} catch (e) { return Promise.reject({
return passCallback({
status: false, status: false,
error: { error: {
message: `Unable to read pass.json as buffer @ ${this.model}. Unable to continue.\n${err}`, message: `Unable to validate pass type or pass file is not a valid buffer. Check the syntax of your pass.json file or refer to https://apple.co/2Nvshvn to use a valid type.`
ecode: 418
}
});
} }
})
}); });
}); });
let _addBuffers = ((err, modelBuffers) => { return Promise.all(L10N.map(f => readdir(path.join(this.model, f)).then(removeHidden)))
if (err) { .then(listByFolder => {
return reject(err); listByFolder.forEach((folder, index) => bundle.push(...folder.map(f => path.join(L10N[index], f))));
}
// I want to get an object containing each buffer associated with its own file name return Promise.all([...bundle.map(f => readFile(path.resolve(this.model, f))), _passExtractor()]).then(buffers => [buffers, bundle]);
let modelFiles = Object.assign(...modelBuffers.map((buf, index) => ({ [bundleList[index]]: buf }))); })
})
.then(([buffers, bundle]) => {
/*
* Parsing the buffers and pushing them into the archive
*/
async.eachOf(modelFiles, (fileBuffer, bufferKey, callback) => { let manifest = {};
let hashAppendTemplate = ((buffer, key) => {
let hashFlow = forge.md.sha1.create(); let hashFlow = forge.md.sha1.create();
hashFlow.update(fileBuffer.toString("binary")); hashFlow.update(buffer.toString("binary"));
manifest[bufferKey] = hashFlow.digest().toHex().trim(); manifest[key] = hashFlow.digest().toHex();
archive.file(path.resolve(this.model, bufferKey), { name: bufferKey });
return callback(); archive.append(buffer, { name: key });
}, _finalize); return Promise.resolve();
}); });
let _finalize = (err => { let passFilesFn = buffers.map((buf, index) => hashAppendTemplate.bind(null, buf, bundle[index])());
if (err) {
return reject({
status: false,
error: {
message: `Unable to compile manifest. ${err}`,
ecode: 418
}
});
}
return Promise.all(passFilesFn).then(() => manifest);
})
.then((manifest) => {
archive.append(JSON.stringify(manifest), { name: "manifest.json" }); archive.append(JSON.stringify(manifest), { name: "manifest.json" });
let signatureBuffer = this._sign(manifest); let signatureBuffer = this._sign(manifest);
@@ -167,37 +125,14 @@ class Pass {
let passStream = new stream.PassThrough(); let passStream = new stream.PassThrough();
archive.pipe(passStream); archive.pipe(passStream);
archive.finalize().then(function() {
return success({ return archive.finalize().then(() => {
return {
status: true, status: true,
content: passStream, content: passStream,
};
}); });
}); });
});
// === execution ===
async.parallel([_passExtractor, ...L10N.extractors], (err, listByFolder) => {
if (err) {
return reject(err);
}
// removing result of passExtractor, which is undefined.
listByFolder.shift();
listByFolder.forEach((folder, index) => bundleList.push(...folder.map(f => path.join(L10N.list[index], f))));
let pathList = bundleList.map(f => path.resolve(this.model, f));
async.concat(pathList, fs.readFile, _addBuffers);
});
})
});
this._parseSettings(this.options)
.then(_gen)
.catch(reject)
});
} }
/** /**