Skip to content

Commit

Permalink
fix: allow replacer and reviver to be null
Browse files Browse the repository at this point in the history
  • Loading branch information
josdejong committed Dec 14, 2023
1 parent c639f49 commit b280774
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 15 deletions.
7 changes: 5 additions & 2 deletions src/parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ test('number', function () {
expect(parse('23')).toEqual(lln('23'))
expect(parse('0')).toEqual(lln('0'))
expect(parse('0e+2')).toEqual(lln('0e+2'))
expect(parse('0e+2').valueOf()).toEqual(0)
expect((parse('0e+2') as LosslessNumber).valueOf()).toEqual(0)
expect(parse('0.0')).toEqual(lln('0.0'))
expect(parse('-0')).toEqual(lln('-0'))
expect(parse('2.3')).toEqual(lln('2.3'))
Expand Down Expand Up @@ -190,7 +190,8 @@ test('reviver - invoke callbacks with key/value and correct context', function (

// convert LosslessNumbers to numbers for easy comparison with native JSON
function toRegularJSON(json: unknown) {
return JSON.parse(stringify(json))
const str = stringify(json)
return str !== undefined ? JSON.parse(str) : undefined
}

function reviver(key: string, value: unknown): unknown {
Expand All @@ -211,6 +212,8 @@ test('reviver - invoke callbacks with key/value and correct context', function (
const logsActual: Log[] = []
parse(text, function (key, value) {
logsActual.push({
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
context: toRegularJSON(this),
key,
value: toRegularJSON(value)
Expand Down
4 changes: 3 additions & 1 deletion src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { GenericObject } from './types'
*/
export function parse(
text: string,
reviver?: Reviver,
reviver?: Reviver | null,
parseNumber: NumberParser = parseLosslessNumber
): unknown {
let i = 0
Expand Down Expand Up @@ -57,6 +57,7 @@ export function parse(
const key = parseString()
if (key === undefined) {
throwObjectKeyExpected()
return // To make TS happy
}

skipWhitespace()
Expand All @@ -65,6 +66,7 @@ export function parse(

if (value === undefined) {
throwObjectValueExpected()
return // To make TS happy
}

// TODO: test deep equal instead of strict equal
Expand Down
16 changes: 10 additions & 6 deletions src/stringify.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { test, expect } from 'vitest'
import Decimal from 'decimal.js'
import { LosslessNumber, stringify } from './index'
import { LosslessNumber, NumberStringifier, stringify } from './index'
import type { GenericObject } from './types'

// helper function to create a lossless number
Expand All @@ -10,6 +10,8 @@ function lln(value: string) {

test('stringify', function () {
expect(stringify(undefined)).toEqual(undefined)
expect(stringify(function () {})).toEqual(undefined)
expect(stringify(Symbol('test'))).toEqual(undefined)

expect(stringify(null)).toEqual('null')

Expand Down Expand Up @@ -106,9 +108,9 @@ test('stringify Date', function () {
})

test('stringify Decimal', function () {
const decimalStringifier = {
test: (value: string) => Decimal.isDecimal(value),
stringify: (value: string) => value.toString()
const decimalStringifier: NumberStringifier = {
test: (value: unknown) => Decimal.isDecimal(value),
stringify: (value: unknown) => (value as Decimal).toString()
}
const numberStringifiers = [decimalStringifier]

Expand All @@ -135,8 +137,8 @@ test('should not have a .toJSON method implemented', function () {

test('should throw an error when the output of a number stringifier is not a number', function () {
const wrongStringifier = {
test: (value: string) => typeof value === 'number',
stringify: (value: string) => 'oopsie' + value // <-- does not return a valid number
test: (value: unknown) => typeof value === 'number',
stringify: (value: unknown) => 'oopsie' + value // <-- does not return a valid number
}

expect(() => stringify([4], undefined, undefined, [wrongStringifier])).toThrow(
Expand Down Expand Up @@ -207,6 +209,8 @@ test('stringify with replacer function', function () {

const logs: Log[] = []
stringify(json, function (key, value) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
logs.push({ context: this, key, value })
return value
})
Expand Down
13 changes: 8 additions & 5 deletions src/stringify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { isNumber } from './utils.js'
*/
export function stringify(
value: unknown,
replacer?: Replacer,
replacer?: Replacer | null,
space?: number | string,
numberStringifiers?: NumberStringifier[]
): string | undefined {
Expand All @@ -51,11 +51,11 @@ export function stringify(
/**
* Stringify a value
*/
function stringifyValue(value: unknown, indent: string): string | undefined {
function stringifyValue(value: unknown, indent: string | undefined): string | undefined {
if (Array.isArray(numberStringifiers)) {
const stringifier = numberStringifiers.find((item) => item.test(value))
if (stringifier) {
const str = stringifier.stringify(value)
const str: unknown = stringifier.stringify(value)
if (typeof str !== 'string' || !isNumber(str)) {
throw new Error(
'Invalid JSON number: ' +
Expand Down Expand Up @@ -109,7 +109,7 @@ export function stringify(
/**
* Stringify an array
*/
function stringifyArray(array: Array<unknown>, indent: string): string {
function stringifyArray(array: Array<unknown>, indent: string | undefined): string {
if (array.length === 0) {
return '[]'
}
Expand Down Expand Up @@ -143,7 +143,10 @@ export function stringify(
/**
* Stringify an object
*/
function stringifyObject(object: GenericObject<unknown>, indent: string): string {
function stringifyObject(
object: GenericObject<unknown>,
indent: string | undefined
): string | undefined {
if (typeof object.toJSON === 'function') {
return stringify(object.toJSON(), replacer, space, undefined)
}
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export function toSafeNumberOrThrow(
? unsafeReason && unsafeReason !== UnsafeNumberReason.truncate_float
: unsafeReason
) {
const unsafeReasonText = unsafeReason.replace(/_\w+$/, '')
const unsafeReasonText = unsafeReason?.replace(/_\w+$/, '')
throw new Error(
'Cannot safely convert to number: ' +
`the value '${value}' would ${unsafeReasonText} and become ${number}`
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"esModuleInterop": true,
"allowJs": false,
"checkJs": false,
"strict": true,
"noImplicitAny": true,
"declaration": true,
"declarationDir": "lib/types",
Expand Down

0 comments on commit b280774

Please sign in to comment.