Skip to content

Commit

Permalink
merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
Jolie Rabideau authored and Jolie Rabideau committed Jan 11, 2024
2 parents 5c4b3b4 + a1d26a9 commit 406d3e8
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 112 deletions.
2 changes: 1 addition & 1 deletion lib/platform-bible-utils/dist/index.cjs.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/platform-bible-utils/dist/index.cjs.js.map

Large diffs are not rendered by default.

43 changes: 19 additions & 24 deletions lib/platform-bible-utils/dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,42 +300,38 @@ export declare function getAllObjectFunctionNames(obj: {
*/
export function deepEqual(a: unknown, b: unknown): boolean;
/**
* Converts a JavaScript value to a JSON string, changing `undefined` properties to `null`
* properties in the JSON string.
* Converts a JavaScript value to a JSON string, changing `undefined` properties in the JavaScript
* object to `null` properties in the JSON string.
*
* WARNING: `null` and `undefined` values are treated as the same thing by this function and will be
* dropped when passed to {@link deserialize}. For example, `{ a: 1, b: undefined, c: null }` will
* become `{ a: 1 }` after passing through {@link serialize} then {@link deserialize}. If you are
* passing around user data that needs to retain `null` and/or `undefined` values, you should wrap
* them yourself in a string before using this function. Alternatively, you can write your own
* replacer that will preserve `null` and `undefined` values in a way that a custom reviver will
* understand when deserializing.
* WARNING: `null` values will become `undefined` values after passing through {@link serialize} then
* {@link deserialize}. For example, `{ a: 1, b: undefined, c: null }` will become `{ a: 1, b:
* undefined, c: undefined }`. If you are passing around user data that needs to retain `null`
* values, you should wrap them yourself in a string before using this function. Alternatively, you
* can write your own replacer that will preserve `null` in a way that you can recover later.
*
* @param value A JavaScript value, usually an object or array, to be converted.
* @param replacer A function that transforms the results. Note that all `null` and `undefined`
* values returned by the replacer will be further transformed into a moniker that deserializes
* into `undefined`.
* @param replacer A function that transforms the results. Note that all `undefined` values returned
* by the replacer will be further transformed into `null` in the JSON string.
* @param space Adds indentation, white space, and line break characters to the return-value JSON
* text to make it easier to read. See the `space` parameter of `JSON.stringify` for more
* details.
*/
export declare function serialize(value: unknown, replacer?: (this: unknown, key: string, value: unknown) => unknown, space?: string | number): string;
/**
* Converts a JSON string into a value.
* Converts a JSON string into a value, converting all `null` properties from JSON into `undefined`
* in the returned JavaScript value/object.
*
* WARNING: `null` and `undefined` values that were serialized by {@link serialize} will both be made
* into `undefined` values by this function. If those values are properties of objects, those
* properties will simply be dropped. For example, `{ a: 1, b: undefined, c: null }` will become `{
* a: 1 }` after passing through {@link serialize} then {@link deserialize}. If you are passing around
* user data that needs to retain `null` and/or `undefined` values, you should wrap them yourself in
* a string before using this function. Alternatively, you can write your own reviver that will
* preserve `null` and `undefined` values in a way that a custom replacer will encode when
* serializing.
* WARNING: `null` values will become `undefined` values after passing through {@link serialize} then
* {@link deserialize}. For example, `{ a: 1, b: undefined, c: null }` will become `{ a: 1, b:
* undefined, c: undefined }`. If you are passing around user data that needs to retain `null`
* values, you should wrap them yourself in a string before using this function. Alternatively, you
* can write your own replacer that will preserve `null` in a way that you can recover later.
*
* @param text A valid JSON string.
* @param reviver A function that transforms the results. This function is called for each member of
* the object. If a member contains nested objects, the nested objects are transformed before the
* parent object is.
* parent object is. Note that `null` values are converted into `undefined` values after the
* reviver has run.
*/
export declare function deserialize(value: string, reviver?: (this: unknown, key: string, value: unknown) => unknown): any;
/**
Expand All @@ -345,8 +341,7 @@ export declare function deserialize(value: string, reviver?: (this: unknown, key
* @returns True if serializable; false otherwise
*
* Note: the values `undefined` and `null` are serializable (on their own or in an array), but
* `undefined` and `null` properties of objects are dropped when serializing/deserializing. That
* means `undefined` and `null` properties on a value passed in will cause it to fail.
* `null` values get transformed into `undefined` when serializing/deserializing.
*
* WARNING: This is inefficient right now as it stringifies, parses, stringifies, and === the value.
* Please only use this if you need to
Expand Down
48 changes: 26 additions & 22 deletions lib/platform-bible-utils/dist/index.es.js

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

2 changes: 1 addition & 1 deletion lib/platform-bible-utils/dist/index.es.js.map

Large diffs are not rendered by default.

76 changes: 41 additions & 35 deletions lib/platform-bible-utils/src/serialization.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
/**
* Converts a JavaScript value to a JSON string, changing `undefined` properties to `null`
* properties in the JSON string.
* Converts a JavaScript value to a JSON string, changing `undefined` properties in the JavaScript
* object to `null` properties in the JSON string.
*
* WARNING: `null` and `undefined` values are treated as the same thing by this function and will be
* dropped when passed to {@link deserialize}. For example, `{ a: 1, b: undefined, c: null }` will
* become `{ a: 1 }` after passing through {@link serialize} then {@link deserialize}. If you are
* passing around user data that needs to retain `null` and/or `undefined` values, you should wrap
* them yourself in a string before using this function. Alternatively, you can write your own
* replacer that will preserve `null` and `undefined` values in a way that a custom reviver will
* understand when deserializing.
* WARNING: `null` values will become `undefined` values after passing through {@link serialize} then
* {@link deserialize}. For example, `{ a: 1, b: undefined, c: null }` will become `{ a: 1, b:
* undefined, c: undefined }`. If you are passing around user data that needs to retain `null`
* values, you should wrap them yourself in a string before using this function. Alternatively, you
* can write your own replacer that will preserve `null` in a way that you can recover later.
*
* @param value A JavaScript value, usually an object or array, to be converted.
* @param replacer A function that transforms the results. Note that all `null` and `undefined`
* values returned by the replacer will be further transformed into a moniker that deserializes
* into `undefined`.
* @param replacer A function that transforms the results. Note that all `undefined` values returned
* by the replacer will be further transformed into `null` in the JSON string.
* @param space Adds indentation, white space, and line break characters to the return-value JSON
* text to make it easier to read. See the `space` parameter of `JSON.stringify` for more
* details.
Expand All @@ -35,39 +32,49 @@ export function serialize(
}

/**
* Converts a JSON string into a value.
* Converts a JSON string into a value, converting all `null` properties from JSON into `undefined`
* in the returned JavaScript value/object.
*
* WARNING: `null` and `undefined` values that were serialized by {@link serialize} will both be made
* into `undefined` values by this function. If those values are properties of objects, those
* properties will simply be dropped. For example, `{ a: 1, b: undefined, c: null }` will become `{
* a: 1 }` after passing through {@link serialize} then {@link deserialize}. If you are passing around
* user data that needs to retain `null` and/or `undefined` values, you should wrap them yourself in
* a string before using this function. Alternatively, you can write your own reviver that will
* preserve `null` and `undefined` values in a way that a custom replacer will encode when
* serializing.
* WARNING: `null` values will become `undefined` values after passing through {@link serialize} then
* {@link deserialize}. For example, `{ a: 1, b: undefined, c: null }` will become `{ a: 1, b:
* undefined, c: undefined }`. If you are passing around user data that needs to retain `null`
* values, you should wrap them yourself in a string before using this function. Alternatively, you
* can write your own replacer that will preserve `null` in a way that you can recover later.
*
* @param text A valid JSON string.
* @param reviver A function that transforms the results. This function is called for each member of
* the object. If a member contains nested objects, the nested objects are transformed before the
* parent object is.
* parent object is. Note that `null` values are converted into `undefined` values after the
* reviver has run.
*/
export function deserialize(
value: string,
reviver?: (this: unknown, key: string, value: unknown) => unknown,
// Need to use `any` instead of `unknown` here to match the signature of JSON.parse
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): any {
const undefinedReviver = (replacerKey: string, replacerValue: unknown) => {
let newValue = replacerValue;
// All `null` values become `undefined` on the way from JSON strings into JS objects
// eslint-disable-next-line no-null/no-null
if (newValue === null) newValue = undefined;
if (reviver) newValue = reviver(replacerKey, newValue);
return newValue;
};
// TODO: Do something like drop our custom reviver and crawl the object tree to replace all null
// properties with undefined properties so that undefined properties don't disappear.
return JSON.parse(value, undefinedReviver);
// Helper function to replace `null` with `undefined` on a per property basis. This can't be done
// with our own reviver because `JSON.parse` removes `undefined` properties from the return value.
function replaceNull(obj: Record<string | number, unknown>): Record<string | number, unknown> {
Object.keys(obj).forEach((key: string | number) => {
// We only want to replace `null`, not other falsy values
// eslint-disable-next-line no-null/no-null
if (obj[key] === null) obj[key] = undefined;
// If the property is an object, recursively call the helper function on it
else if (typeof obj[key] === 'object')
// Since the object came from a string, we know the keys will not be symbols
// eslint-disable-next-line no-type-assertion/no-type-assertion
obj[key] = replaceNull(obj[key] as Record<string | number, unknown>);
});
return obj;
}

const parsedObject = JSON.parse(value, reviver);
// Explicitly convert the value 'null' that isn't stored as a property on an object to 'undefined'
// eslint-disable-next-line no-null/no-null
if (parsedObject === null) return undefined;
if (typeof parsedObject === 'object') return replaceNull(parsedObject);
return parsedObject;
}

/**
Expand All @@ -77,8 +84,7 @@ export function deserialize(
* @returns True if serializable; false otherwise
*
* Note: the values `undefined` and `null` are serializable (on their own or in an array), but
* `undefined` and `null` properties of objects are dropped when serializing/deserializing. That
* means `undefined` and `null` properties on a value passed in will cause it to fail.
* `null` values get transformed into `undefined` when serializing/deserializing.
*
* WARNING: This is inefficient right now as it stringifies, parses, stringifies, and === the value.
* Please only use this if you need to
Expand Down
15 changes: 7 additions & 8 deletions src/renderer/hooks/hook-generators/create-use-data-hook.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import IDataProvider from '@shared/models/data-provider.interface';
import { useEventAsync } from 'platform-bible-react';
import { useMemo, useRef, useState } from 'react';
import { PlatformEventAsync, PlatformEventHandler, isString } from 'platform-bible-utils';
import { isString, PlatformEventAsync, PlatformEventHandler } from 'platform-bible-utils';
import ExtractDataProviderDataTypes from '@shared/models/extract-data-provider-data-types.model';

/**
Expand Down Expand Up @@ -131,7 +131,7 @@ function createUseDataHook<TUseDataProviderParams extends unknown[]>(
// index subscribe. Assert to specified generic type.
/* eslint-disable @typescript-eslint/no-explicit-any, no-type-assertion/no-type-assertion */
await (
dataProvider[
(dataProvider as any)[
`subscribe${dataType as DataTypeNames<TDataTypes>}`
] as DataProviderSubscriber<TDataTypes[TDataType]>
)(
Expand All @@ -142,7 +142,7 @@ function createUseDataHook<TUseDataProviderParams extends unknown[]>(
// When we receive updated data, mark that we are not loading
setIsLoading(false);
},
subscriberOptions,
subscriberOptionsRef.current,
);

return async () => {
Expand All @@ -152,7 +152,7 @@ function createUseDataHook<TUseDataProviderParams extends unknown[]>(
};
}
: undefined,
[dataProvider, selector, subscriberOptions],
[dataProvider, selector],
);

// Subscribe to the data provider
Expand All @@ -171,10 +171,9 @@ function createUseDataHook<TUseDataProviderParams extends unknown[]>(
// subscribe. Assert to specified generic type.
/* eslint-disable @typescript-eslint/no-explicit-any, no-type-assertion/no-type-assertion */
(
dataProvider[`set${dataType as DataTypeNames<TDataTypes>}`] as DataProviderSetter<
TDataTypes,
typeof dataType
>
(dataProvider as any)[
`set${dataType as DataTypeNames<TDataTypes>}`
] as DataProviderSetter<TDataTypes, typeof dataType>
)(
/* eslint-enable */
selector,
Expand Down
Loading

0 comments on commit 406d3e8

Please sign in to comment.