Skip to content

Commit

Permalink
Upgrade safe-stable-stringify lib to 2.x
Browse files Browse the repository at this point in the history
https://github.com/BridgeAR/safe-stable-stringify/releases

- Allow `opts` to configure the lib.
- Update types for new opts.
- Add tests to fill in coverage for json.js.

NB. The `Buffer.toString('base64')` conversion was removed. Buffer implements `toJSON` so this block of code was not being executed anyway, as the replacer is called with the result of `toJSON`. Base64 was never being returned.
https://nodejs.org/api/buffer.html#buftojson
  • Loading branch information
mastermatt committed Jan 27, 2022
1 parent 80547d6 commit c5df278
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 15 deletions.
36 changes: 36 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,42 @@ export interface JsonOptions {
* The number of white space used to format the json.
*/
space?: number;

// The following options come from safe-stable-stringify
// https://github.com/BridgeAR/safe-stable-stringify/blob/main/index.d.ts

/**
* If `true`, bigint values are converted to a number. Otherwise, they are ignored.
* This option is ignored by default as Logform stringifies BigInt in the default replacer.
* @default true
*/
bigint?: boolean,
/**
* Defines the value for circular references.
* Set to `undefined`, circular properties are not serialized (array entries are replaced with null).
* Set to `Error`, to throw on circular references.
* @default "[Circular]"
*/
circularValue?: string | null | TypeErrorConstructor | ErrorConstructor,
/**
* If `true`, guarantee a deterministic key order instead of relying on the insertion order.
* @default true
*/
deterministic?: boolean,
/**
* Maximum number of entries to serialize per object (at least one).
* The serialized output contains information about how many entries have not been serialized.
* Ignored properties are counted as well (e.g., properties with symbol values).
* Using the array replacer overrules this option.
* @default Infinity
*/
maximumBreadth?: number,
/**
* Maximum number of object nesting levels (at least 1) that will be serialized.
* Objects at the maximum level are serialized as `"[Object]"` and arrays as `"[Array]"`.
* @default Infinity
*/
maximumDepth?: number,
}

export interface LabelOptions {
Expand Down
10 changes: 6 additions & 4 deletions json.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

const format = require('./format');
const { MESSAGE } = require('triple-beam');
const jsonStringify = require('safe-stable-stringify');
const stringify = require('safe-stable-stringify');

/*
* function replacer (key, value)
* Handles proper stringification of Buffer and bigint output.
*/
function replacer(key, value) {
if (value instanceof Buffer)
return value.toString('base64');
// safe-stable-stringify does support BigInt, however, it doesn't wrap the value in quotes.
// Leading to lose in fidelity if the resulting string is parsed.
// It would also be a breaking change for logform.
// eslint-disable-next-line valid-typeof
if (typeof value === 'bigint')
return value.toString();
Expand All @@ -23,7 +24,8 @@ function replacer(key, value) {
* object into pure JSON. This was previously exposed as { json: true }
* to transports in `winston < 3.0.0`.
*/
module.exports = format((info, opts = {}) => {
module.exports = format((info, opts) => {
const jsonStringify = stringify.configure(opts);
info[MESSAGE] = jsonStringify(info, opts.replacer || replacer, opts.space);
return info;
});
21 changes: 12 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"homepage": "https://github.com/winstonjs/logform#readme",
"dependencies": {
"colors": "1.4.0",
"safe-stable-stringify": "^1.1.0",
"safe-stable-stringify": "^2.3.1",
"fecha": "^4.2.0",
"ms": "^2.1.1",
"triple-beam": "^1.3.0"
Expand Down
55 changes: 54 additions & 1 deletion test/json.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ describe('json', () => {
}
));


it('json() can handle circular JSON objects', (done) => {
// Define an info with a circular reference.
const circular = { level: 'info', message: 'has a circular ref ok!', filtered: true };
Expand All @@ -74,5 +73,59 @@ describe('json', () => {
stream.write(fmt.transform(circular, fmt.options));
});

// https://nodejs.org/api/buffer.html#buftojson
it('json() can handle Buffers', assumeFormatted(
json(),
{ level: 'info', message: 'has a buffer!', buf: Buffer.from('foo') },
(info) => {
assume(info.level).is.a('string');
assume(info.level).equals('info');

const parsed = JSON.parse(info[MESSAGE]);
assume(parsed).deep.equal({
level: 'info',
message: 'has a buffer!',
buf: {
type: 'Buffer',
data: [102, 111, 111]
}
});
}
));

it('json() can handle BigInt', assumeFormatted(
json(),
// the version of ESLint we're on doesn't understand BigInt
// eslint-disable-next-line
{ level: 'info', message: 'has a BigInt!', howBig: BigInt(9007199254740991) },
(info) => {
assume(info.level).is.a('string');
assume(info.level).equals('info');

const parsed = JSON.parse(info[MESSAGE]);
assume(parsed).deep.equal({
level: 'info',
message: 'has a BigInt!',
howBig: '9007199254740991'
});
}
));

it('json() can pass safe-stable-stringify options', assumeFormatted(
json({ maximumDepth: 1 }),
{ level: 'info', message: 'has a BigInt!', nested: { foo: 'bar' }},
(info) => {
assume(info.level).is.a('string');
assume(info.level).equals('info');

const parsed = JSON.parse(info[MESSAGE]);
assume(parsed).deep.equal({
level: 'info',
message: 'has a BigInt!',
nested: '[Object]'
});
}
));

it('exposes the Format prototype', assumeHasPrototype(json));
});

0 comments on commit c5df278

Please sign in to comment.