Repository: pinojs/pino-pretty
Pull request #312 (2022-03-03 09:13:52)

build: reduce dependabot update frequency
See https://github.com/pinojs/pino/pull/1356

coveralls: ## Pull Request Test Coverage Report for [Build 1927115915](https://coveralls.io/builds/47040445)

* **0** of **0**   changed or added relevant lines in **0** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage remained the same at **100.0%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/47040445/badge)](https://coveralls.io/builds/47040445) |
| :-- | --: |
| Change from base [Build 1926958611](https://coveralls.io/builds/47039469): |  0.0% |
| Covered Lines: | 350 |
| Relevant Lines: | 350 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index e50fb48..dfa7fa6 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,16 +1,13 @@
 version: 2
 updates:
-- package-ecosystem: github-actions
-  directory: '/'
-  ignore:
-      - dependency-name: 'actions/*'
-        update-types:
-            ['version-update:semver-minor', 'version-update:semver-patch']
-  schedule:
-    interval: daily
-  open-pull-requests-limit: 10
-- package-ecosystem: npm
-  directory: '/'
-  schedule:
-    interval: daily
-  open-pull-requests-limit: 10
\ No newline at end of file
+  - package-ecosystem: "github-actions"
+    directory: "/"
+    schedule:
+      interval: "monthly"
+    open-pull-requests-limit: 10
+
+  - package-ecosystem: "npm"
+    directory: "/"
+    schedule:
+      interval: "weekly"
+    open-pull-requests-limit: 10
diff --git a/index.js b/index.js
index 87234f9..663bd72 100644
--- a/index.js
+++ b/index.js
@@ -4,9 +4,7 @@ const { isColorSupported } = require('colorette')
 const pump = require('pump')
 const { Transform } = require('readable-stream')
 const abstractTransport = require('pino-abstract-transport')
-const sonic = require('sonic-boom')
 const sjs = require('secure-json-parse')
-
 const colors = require('./lib/colors')
 const { ERROR_LIKE_KEYS, MESSAGE_KEY, TIMESTAMP_KEY, LEVEL_KEY, LEVEL_NAMES } = require('./lib/constants')
 const {
@@ -17,6 +15,7 @@ const {
   prettifyMetadata,
   prettifyObject,
   prettifyTime,
+  buildSafeSonicBoom,
   filterLog
 } = require('./lib/utils')
 
@@ -227,7 +226,7 @@ function build (opts = {}) {
     if (typeof opts.destination === 'object' && typeof opts.destination.write === 'function') {
       destination = opts.destination
     } else {
-      destination = sonic({
+      destination = buildSafeSonicBoom({
         dest: opts.destination || 1,
         append: opts.append,
         mkdir: opts.mkdir,
diff --git a/lib/utils.js b/lib/utils.js
index 706f5e8..6de61c4 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -2,7 +2,9 @@
 
 const clone = require('rfdc')({ circles: true })
 const dateformat = require('dateformat')
+const SonicBoom = require('sonic-boom')
 const stringifySafe = require('fast-safe-stringify')
+const { isMainThread } = require('worker_threads')
 const defaultColorizer = require('./colors')()
 const {
   DATE_FORMAT,
@@ -23,6 +25,7 @@ module.exports = {
   prettifyMetadata,
   prettifyObject,
   prettifyTime,
+  buildSafeSonicBoom,
   filterLog
 }
 
@@ -580,3 +583,67 @@ function filterLog (log, ignoreKeys) {
   })
   return logCopy
 }
+
+function noop () {}
+
+/**
+ * Creates a safe SonicBoom instance
+ *
+ * @param {object} opts Options for SonicBoom
+ *
+ * @returns {object} A new SonicBoom stream
+ */
+function buildSafeSonicBoom (opts) {
+  const stream = new SonicBoom(opts)
+  stream.on('error', filterBrokenPipe)
+  // if we are sync: false, we must flush on exit
+  if (!opts.sync && isMainThread) {
+    setupOnExit(stream)
+  }
+  return stream
+
+  function filterBrokenPipe (err) {
+    if (err.code === 'EPIPE') {
+      stream.write = noop
+      stream.end = noop
+      stream.flushSync = noop
+      stream.destroy = noop
+      return
+    }
+    stream.removeListener('error', filterBrokenPipe)
+  }
+}
+
+function setupOnExit (stream) {
+  /* istanbul ignore next */
+  if (global.WeakRef && global.WeakMap && global.FinalizationRegistry) {
+    // This is leak free, it does not leave event handlers
+    const onExit = require('on-exit-leak-free')
+
+    onExit.register(stream, autoEnd)
+
+    stream.on('close', function () {
+      onExit.unregister(stream)
+    })
+  }
+}
+
+/* istanbul ignore next */
+function autoEnd (stream, eventName) {
+  // This check is needed only on some platforms
+
+  if (stream.destroyed) {
+    return
+  }
+
+  if (eventName === 'beforeExit') {
+    // We still have an event loop, let's use it
+    stream.flush()
+    stream.on('drain', function () {
+      stream.end()
+    })
+  } else {
+    // We do not have an event loop, so flush synchronously
+    stream.flushSync()
+  }
+}
diff --git a/package.json b/package.json
index 7416f0b..27cc5d1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "pino-pretty",
-  "version": "7.5.3",
+  "version": "7.5.4",
   "description": "Prettifier for Pino log lines",
   "type": "commonjs",
   "main": "index.js",
@@ -37,6 +37,7 @@
     "dateformat": "^4.6.3",
     "fast-safe-stringify": "^2.0.7",
     "joycon": "^3.1.1",
+    "on-exit-leak-free": "^0.2.0",
     "pino-abstract-transport": "^0.5.0",
     "pump": "^3.0.0",
     "readable-stream": "^3.6.0",
@@ -52,7 +53,7 @@
     "rimraf": "^3.0.2",
     "snazzy": "^9.0.0",
     "standard": "^16.0.3",
-    "tap": "^15.0.9",
+    "tap": "^16.0.0",
     "tsd": "^0.19.0",
     "typescript": "^4.4.3"
   },


Test patch:
diff --git a/test/lib/utils.public.test.js b/test/lib/utils.public.test.js
index 0acbc2e..fb2b175 100644
--- a/test/lib/utils.public.test.js
+++ b/test/lib/utils.public.test.js
@@ -3,6 +3,9 @@
 const tap = require('tap')
 const getColorizer = require('../../lib/colors')
 const utils = require('../../lib/utils')
+const rimraf = require('rimraf')
+const { join } = require('path')
+const fs = require('fs')
 
 tap.test('prettifyErrorLog', t => {
   const { prettifyErrorLog } = utils
@@ -428,3 +431,48 @@ tap.test('#filterLog with circular references', t => {
 
   t.end()
 })
+
+tap.test('buildSafeSonicBoom', t => {
+  const { buildSafeSonicBoom } = utils
+
+  function noop () {}
+
+  const file = () => {
+    const dest = join(__dirname, `${process.pid}-${process.hrtime().toString()}`)
+    const fd = fs.openSync(dest, 'w')
+    return { dest, fd }
+  }
+
+  t.test('should not write when error emitted and code is "EPIPE"', async t => {
+    t.plan(1)
+
+    const { fd, dest } = file()
+    const stream = buildSafeSonicBoom({ sync: true, fd, mkdir: true })
+    t.teardown(() => rimraf(dest, noop))
+
+    stream.emit('error', { code: 'EPIPE' })
+    stream.write('will not work')
+
+    const dataFile = fs.readFileSync(dest)
+    t.equal(dataFile.length, 0)
+  })
+
+  t.test('should stream.write works when error code is not "EPIPE"', async t => {
+    t.plan(3)
+    const { fd, dest } = file()
+    const stream = buildSafeSonicBoom({ sync: true, fd, mkdir: true })
+
+    t.teardown(() => rimraf(dest, noop))
+
+    stream.on('error', () => t.pass('error emitted'))
+
+    stream.emit('error', 'fake error description')
+
+    t.ok(stream.write('will work'))
+
+    const dataFile = fs.readFileSync(dest)
+    t.equal(dataFile.toString(), 'will work')
+  })
+
+  t.end()
+})