Skip to content

Commit

Permalink
Refactor prettify-object to use context
Browse files Browse the repository at this point in the history
  • Loading branch information
jsumners committed Aug 31, 2023
1 parent b4190c6 commit a74b7b1
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 54 deletions.
11 changes: 3 additions & 8 deletions lib/pretty.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ function pretty (inputData) {
const prettifiedErrorLog = prettifyErrorLog({ log, context: this.context })
if (this.singleLine) line += this.EOL
line += prettifiedErrorLog
} else if (!this.hideObject) {
} else if (this.hideObject === false) {
const skipKeys = [
this.messageKey,
this.levelKey,
Expand All @@ -153,14 +153,9 @@ function pretty (inputData) {
typeof log[key] === 'boolean'
})
const prettifiedObject = prettifyObject({
input: log,
log,
skipKeys,
customPrettifiers: this.customPrettifiers,
errorLikeKeys: this.errorLikeObjectKeys,
eol: this.EOL,
ident: this.IDENT,
singleLine: this.singleLine,
colorizer: this.objectColorizer
context: this.context
})

// In single line mode, include a space only if prettified version isn't empty
Expand Down
10 changes: 8 additions & 2 deletions lib/utils/prettify-error-log.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ function prettifyErrorLog ({ log, context }) {
const {
EOL: eol,
IDENT: ident,
errorLikeKeys,
errorProps: errorProperties,
messageKey
} = context
Expand All @@ -55,7 +54,14 @@ function prettifyErrorLog ({ log, context }) {
// The nested object may have "logger" type keys but since they are not
// at the root level of the object being processed, we want to print them.
// Thus, we invoke with `excludeLoggerKeys: false`.
const prettifiedObject = prettifyObject({ input: log[key], errorLikeKeys, excludeLoggerKeys: false, eol, ident: ident + ident })
const prettifiedObject = prettifyObject({
log: log[key],
excludeLoggerKeys: false,
context: {
...context,
IDENT: ident + ident
}
})
result = `${result}${ident}${key}: {${eol}${prettifiedObject}${ident}}${eol}`
continue
}
Expand Down
3 changes: 2 additions & 1 deletion lib/utils/prettify-error-log.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const {
const context = {
EOL: '\n',
IDENT: ' ',
errorLikeKeys: ERROR_LIKE_KEYS,
customPrettifiers: {},
errorLikeObjectKeys: ERROR_LIKE_KEYS,
errorProps: [],
messageKey: MESSAGE_KEY
}
Expand Down
44 changes: 19 additions & 25 deletions lib/utils/prettify-object.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,22 @@
module.exports = prettifyObject

const {
ERROR_LIKE_KEYS,
LOGGER_KEYS
} = require('../constants')
const defaultColorizer = require('../colors')()

const stringifySafe = require('fast-safe-stringify')
const joinLinesWithIndentation = require('./join-lines-with-indentation')
const prettifyError = require('./prettify-error')

/**
* @typedef {object} PrettifyObjectParams
* @property {object} input The object to prettify.
* @property {string} [ident] The indentation sequence to use. Default: `' '`.
* @property {string} [eol] The EOL sequence to use. Default: `'\n'`.
* @property {string[]} [skipKeys] A set of object keys to exclude from the
* prettified result. Default: `[]`.
* @property {CustomPrettifiers} [customPrettifiers] Dictionary of
* custom prettifiers. Default: `{}`.
* @property {string[]} [errorLikeKeys] A set of object keys that contain
* error objects. Default: `ERROR_LIKE_KEYS` constant.
* @property {object} log The object to prettify.
* @property {boolean} [excludeLoggerKeys] Indicates if known logger specific
* keys should be excluded from prettification. Default: `true`.
* @property {boolean} [singleLine] Should non-error keys all be formatted
* on a single line? This does NOT apply to errors, which will still be
* multi-line. Default: `false`
* @property {string[]} [skipKeys] A set of object keys to exclude from the
* * prettified result. Default: `[]`.
* @property {PrettyContext} context The context object built from parsing
* the options.
*/

/**
Expand All @@ -41,16 +32,19 @@ const prettifyError = require('./prettify-error')
* there was nothing to prettify.
*/
function prettifyObject ({
input,
ident = ' ',
eol = '\n',
skipKeys = [],
customPrettifiers = {},
errorLikeKeys = ERROR_LIKE_KEYS,
log,
excludeLoggerKeys = true,
singleLine = false,
colorizer = defaultColorizer
skipKeys = [],
context
}) {
const {
EOL: eol,
IDENT: ident,
customPrettifiers,
errorLikeObjectKeys: errorLikeKeys,
objectColorizer,
singleLine
} = context
const keysToIgnore = [].concat(skipKeys)

/* istanbul ignore else */
Expand All @@ -59,11 +53,11 @@ function prettifyObject ({
let result = ''

// Split object keys into two categories: error and non-error
const { plain, errors } = Object.entries(input).reduce(({ plain, errors }, [k, v]) => {
const { plain, errors } = Object.entries(log).reduce(({ plain, errors }, [k, v]) => {
if (keysToIgnore.includes(k) === false) {
// Pre-apply custom prettifiers, because all 3 cases below will need this
const pretty = typeof customPrettifiers[k] === 'function'
? customPrettifiers[k](v, k, input)
? customPrettifiers[k](v, k, log)
: v
if (errorLikeKeys.includes(k)) {
errors[k] = pretty
Expand All @@ -78,7 +72,7 @@ function prettifyObject ({
// Stringify the entire object as a single JSON line
/* istanbul ignore else */
if (Object.keys(plain).length > 0) {
result += colorizer.greyMessage(stringifySafe(plain))
result += objectColorizer.greyMessage(stringifySafe(plain))
}
result += eol
// Avoid printing the escape character on escaped backslashes.
Expand Down
82 changes: 66 additions & 16 deletions lib/utils/prettify-object.test.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,63 @@
'use strict'

const tap = require('tap')
const colors = require('../colors')
const prettifyObject = require('./prettify-object')
const {
ERROR_LIKE_KEYS
} = require('../constants')

const context = {
EOL: '\n',
IDENT: ' ',
customPrettifiers: {},
errorLikeObjectKeys: ERROR_LIKE_KEYS,
objectColorizer: colors(),
singleLine: false
}

tap.test('returns empty string if no properties present', async t => {
const str = prettifyObject({ input: {} })
const str = prettifyObject({ log: {}, context })
t.equal(str, '')
})

tap.test('works with single level properties', async t => {
const str = prettifyObject({ input: { foo: 'bar' } })
const str = prettifyObject({ log: { foo: 'bar' }, context })
t.equal(str, ' foo: "bar"\n')
})

tap.test('works with multiple level properties', async t => {
const str = prettifyObject({ input: { foo: { bar: 'baz' } } })
const str = prettifyObject({ log: { foo: { bar: 'baz' } }, context })
t.equal(str, ' foo: {\n "bar": "baz"\n }\n')
})

tap.test('skips specified keys', async t => {
const str = prettifyObject({ input: { foo: 'bar', hello: 'world' }, skipKeys: ['foo'] })
const str = prettifyObject({
log: { foo: 'bar', hello: 'world' },
skipKeys: ['foo'],
context
})
t.equal(str, ' hello: "world"\n')
})

tap.test('ignores predefined keys', async t => {
const str = prettifyObject({ input: { foo: 'bar', pid: 12345 } })
const str = prettifyObject({ log: { foo: 'bar', pid: 12345 }, context })
t.equal(str, ' foo: "bar"\n')
})

tap.test('ignores escaped backslashes in string values', async t => {
const str = prettifyObject({ input: { foo_regexp: '\\[^\\w\\s]\\' } })
const str = prettifyObject({ log: { foo_regexp: '\\[^\\w\\s]\\' }, context })
t.equal(str, ' foo_regexp: "\\[^\\w\\s]\\"\n')
})

tap.test('ignores escaped backslashes in string values (singleLine option)', async t => {
const str = prettifyObject({ input: { foo_regexp: '\\[^\\w\\s]\\' }, singleLine: true })
const str = prettifyObject({
log: { foo_regexp: '\\[^\\w\\s]\\' },
context: {
...context,
singleLine: true
}
})
t.equal(str, '{"foo_regexp":"\\[^\\w\\s]\\"}\n')
})

Expand All @@ -44,7 +67,7 @@ tap.test('works with error props', async t => {
message: err.message,
stack: err.stack
}
const str = prettifyObject({ input: { error: serializedError } })
const str = prettifyObject({ log: { error: serializedError }, context })
t.ok(str.startsWith(' error:'))
t.ok(str.includes(' "message": "Something went wrong",'))
t.ok(str.includes(' Error: Something went wrong'))
Expand All @@ -54,25 +77,40 @@ tap.test('customPrettifiers gets applied', async t => {
const customPrettifiers = {
foo: v => v.toUpperCase()
}
const str = prettifyObject({ input: { foo: 'foo' }, customPrettifiers })
const str = prettifyObject({
log: { foo: 'foo' },
context: {
...context,
customPrettifiers
}
})
t.equal(str.startsWith(' foo: FOO'), true)
})

tap.test('skips lines omitted by customPrettifiers', async t => {
const customPrettifiers = {
foo: () => { return undefined }
}
const str = prettifyObject({ input: { foo: 'foo', bar: 'bar' }, customPrettifiers })
const str = prettifyObject({
log: { foo: 'foo', bar: 'bar' },
context: {
...context,
customPrettifiers
}
})
t.equal(str.includes('bar: "bar"'), true)
t.equal(str.includes('foo: "foo"'), false)
})

tap.test('joined lines omits starting eol', async t => {
const str = prettifyObject({
input: { msg: 'doing work', calls: ['step 1', 'step 2', 'step 3'], level: 30 },
ident: '',
customPrettifiers: {
calls: val => '\n' + val.map(it => ' ' + it).join('\n')
log: { msg: 'doing work', calls: ['step 1', 'step 2', 'step 3'], level: 30 },
context: {
...context,
IDENT: '',
customPrettifiers: {
calls: val => '\n' + val.map(it => ' ' + it).join('\n')
}
}
})
t.equal(str, [
Expand All @@ -89,14 +127,26 @@ tap.test('errors skips prettifiers', async t => {
const customPrettifiers = {
err: () => { return 'is_err' }
}
const str = prettifyObject({ input: { err: Error('boom') }, customPrettifiers })
const str = prettifyObject({
log: { err: Error('boom') },
context: {
...context,
customPrettifiers
}
})
t.equal(str.includes('err: is_err'), true)
})

tap.test('errors skips prettifying if no lines are present', async t => {
const customPrettifiers = {
err: () => { return undefined }
}
const str = prettifyObject({ input: { err: Error('boom') }, customPrettifiers })
const str = prettifyObject({
log: { err: Error('boom') },
context: {
...context,
customPrettifiers
}
})
t.equal(str, '')
})
4 changes: 2 additions & 2 deletions test/error-objects.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ test('error like objects tests', (t) => {
' statusCode: 500',
' originalStack: original stack',
' dataBaseSpecificError: {',
' erroMessage: "some database error message"',
' errorMessage: "some database error message"',
' evenMoreSpecificStuff: {',
' "someErrorRelatedObject": "error"',
' }',
Expand All @@ -331,7 +331,7 @@ test('error like objects tests', (t) => {
error.statusCode = 500
error.originalStack = 'original stack'
error.dataBaseSpecificError = {
erroMessage: 'some database error message',
errorMessage: 'some database error message',
evenMoreSpecificStuff: {
someErrorRelatedObject: 'error'
}
Expand Down

0 comments on commit a74b7b1

Please sign in to comment.