1const npmlog = require('npmlog') 2 3module.exports = cls => class Tracker extends cls { 4 #progress = new Map() 5 #setProgress 6 7 constructor (options = {}) { 8 super(options) 9 this.#setProgress = !!options.progress 10 } 11 12 addTracker (section, subsection = null, key = null) { 13 if (section === null || section === undefined) { 14 this.#onError(`Tracker can't be null or undefined`) 15 } 16 17 if (key === null) { 18 key = subsection 19 } 20 21 const hasTracker = this.#progress.has(section) 22 const hasSubtracker = this.#progress.has(`${section}:${key}`) 23 24 if (hasTracker && subsection === null) { 25 // 0. existing tracker, no subsection 26 this.#onError(`Tracker "${section}" already exists`) 27 } else if (!hasTracker && subsection === null) { 28 // 1. no existing tracker, no subsection 29 // Create a new tracker from npmlog 30 // starts progress bar 31 if (this.#setProgress && this.#progress.size === 0) { 32 npmlog.enableProgress() 33 } 34 35 this.#progress.set(section, npmlog.newGroup(section)) 36 } else if (!hasTracker && subsection !== null) { 37 // 2. no parent tracker and subsection 38 this.#onError(`Parent tracker "${section}" does not exist`) 39 } else if (!hasTracker || !hasSubtracker) { 40 // 3. existing parent tracker, no subsection tracker 41 // Create a new subtracker in this.#progress from parent tracker 42 this.#progress.set(`${section}:${key}`, 43 this.#progress.get(section).newGroup(`${section}:${subsection}`) 44 ) 45 } 46 // 4. existing parent tracker, existing subsection tracker 47 // skip it 48 } 49 50 finishTracker (section, subsection = null, key = null) { 51 if (section === null || section === undefined) { 52 this.#onError(`Tracker can't be null or undefined`) 53 } 54 55 if (key === null) { 56 key = subsection 57 } 58 59 const hasTracker = this.#progress.has(section) 60 const hasSubtracker = this.#progress.has(`${section}:${key}`) 61 62 // 0. parent tracker exists, no subsection 63 // Finish parent tracker and remove from this.#progress 64 if (hasTracker && subsection === null) { 65 // check if parent tracker does 66 // not have any remaining children 67 const keys = this.#progress.keys() 68 for (const key of keys) { 69 if (key.match(new RegExp(section + ':'))) { 70 this.finishTracker(section, key) 71 } 72 } 73 74 // remove parent tracker 75 this.#progress.get(section).finish() 76 this.#progress.delete(section) 77 78 // remove progress bar if all 79 // trackers are finished 80 if (this.#setProgress && this.#progress.size === 0) { 81 npmlog.disableProgress() 82 } 83 } else if (!hasTracker && subsection === null) { 84 // 1. no existing parent tracker, no subsection 85 this.#onError(`Tracker "${section}" does not exist`) 86 } else if (!hasTracker || hasSubtracker) { 87 // 2. subtracker exists 88 // Finish subtracker and remove from this.#progress 89 this.#progress.get(`${section}:${key}`).finish() 90 this.#progress.delete(`${section}:${key}`) 91 } 92 // 3. existing parent tracker, no subsection 93 } 94 95 #onError (msg) { 96 if (this.#setProgress) { 97 npmlog.disableProgress() 98 } 99 throw new Error(msg) 100 } 101} 102