timers.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. const EE = require('node:events')
  2. const fs = require('node:fs')
  3. const { log, time } = require('proc-log')
  4. const INITIAL_TIMER = 'npm'
  5. class Timers extends EE {
  6. #file
  7. #timing
  8. #unfinished = new Map()
  9. #finished = {}
  10. constructor () {
  11. super()
  12. this.on()
  13. time.start(INITIAL_TIMER)
  14. this.started = this.#unfinished.get(INITIAL_TIMER)
  15. }
  16. on () {
  17. process.on('time', this.#timeHandler)
  18. }
  19. off () {
  20. process.off('time', this.#timeHandler)
  21. }
  22. load ({ path, timing } = {}) {
  23. this.#timing = timing
  24. this.#file = `${path}timing.json`
  25. }
  26. finish (metadata) {
  27. time.end(INITIAL_TIMER)
  28. for (const [name, timer] of this.#unfinished) {
  29. log.silly('unfinished npm timer', name, timer)
  30. }
  31. if (!this.#timing) {
  32. // Not in timing mode, nothing else to do here
  33. return
  34. }
  35. try {
  36. this.#writeFile(metadata)
  37. log.info('timing', `Timing info written to: ${this.#file}`)
  38. } catch (e) {
  39. log.warn('timing', `could not write timing file: ${e}`)
  40. }
  41. }
  42. #writeFile (metadata) {
  43. const globalStart = this.started
  44. const globalEnd = this.#finished[INITIAL_TIMER]
  45. const content = {
  46. metadata,
  47. timers: this.#finished,
  48. // add any unfinished timers with their relative start/end
  49. unfinishedTimers: [...this.#unfinished.entries()].reduce((acc, [name, start]) => {
  50. acc[name] = [start - globalStart, globalEnd - globalStart]
  51. return acc
  52. }, {}),
  53. }
  54. fs.writeFileSync(this.#file, JSON.stringify(content) + '\n')
  55. }
  56. #timeHandler = (level, name) => {
  57. const now = Date.now()
  58. switch (level) {
  59. case time.KEYS.start:
  60. this.#unfinished.set(name, now)
  61. break
  62. case time.KEYS.end: {
  63. if (this.#unfinished.has(name)) {
  64. const ms = now - this.#unfinished.get(name)
  65. this.#finished[name] = ms
  66. this.#unfinished.delete(name)
  67. log.timing(name, `Completed in ${ms}ms`)
  68. } else {
  69. log.silly('timing', `Tried to end timer that doesn't exist: ${name}`)
  70. }
  71. }
  72. }
  73. }
  74. }
  75. module.exports = Timers