@ -3,22 +3,54 @@ import { DEV } from 'esm-env';
import * as w from './warnings.js' ;
import { get _prototype _of , is _array , object _prototype } from './utils.js' ;
/ * *
* In dev , we keep track of which properties could not be cloned . In prod
* we don ' t bother , but we keep a dummy array around so that the
* signature stays the same
* @ type { string [ ] }
* /
const empty = [ ] ;
/ * *
* @ template T
* @ param { T } value
* @ returns { Snapshot < T > }
* /
export function snapshot ( value ) {
return clone ( value , new Map ( ) ) ;
if ( DEV ) {
/** @type {string[]} */
const paths = [ ] ;
const copy = clone ( value , new Map ( ) , '' , paths ) ;
if ( paths . length === 1 && paths [ 0 ] === '' ) {
// value could not be cloned
w . state _snapshot _uncloneable ( ) ;
} else if ( paths . length > 0 ) {
// some properties could not be cloned
const slice = paths . length > 10 ? paths . slice ( 0 , 7 ) : paths . slice ( 0 , 10 ) ;
const excess = paths . length - slice . length ;
let uncloned = slice . map ( ( path ) => ` - <value> ${ path } ` ) . join ( '\n' ) ;
if ( excess > 0 ) uncloned += ` \n - ...and ${ excess } more ` ;
w . state _snapshot _uncloneable ( uncloned ) ;
}
return copy ;
}
return clone ( value , new Map ( ) , '' , empty ) ;
}
/ * *
* @ template T
* @ param { T } value
* @ param { Map < T , Snapshot < T >> } cloned
* @ param { string } path
* @ param { string [ ] } paths
* @ returns { Snapshot < T > }
* /
function clone ( value , cloned ) {
function clone ( value , cloned , path , paths ) {
if ( typeof value === 'object' && value !== null ) {
const unwrapped = cloned . get ( value ) ;
if ( unwrapped !== undefined ) return unwrapped ;
@ -27,8 +59,8 @@ function clone(value, cloned) {
const copy = /** @type {Snapshot<any>} */ ( [ ] ) ;
cloned . set ( value , copy ) ;
for ( const element of value ) {
copy . push ( clone ( element, cloned ) ) ;
for ( let i = 0 ; i < value . length ; i += 1 ) {
copy . push ( clone ( value[ i ] , cloned , DEV ? ` ${ path } [ ${ i } ] ` : path , paths ) ) ;
}
return copy ;
@ -41,14 +73,19 @@ function clone(value, cloned) {
for ( var key in value ) {
// @ts-expect-error
copy [ key ] = clone ( value [ key ] , cloned );
copy [ key ] = clone ( value [ key ] , cloned , DEV ? ` ${ path } . ${ key } ` : path , paths );
}
return copy ;
}
if ( typeof ( /** @type {T & { toJSON?: any } } */ ( value ) . toJSON ) === 'function' ) {
return clone ( /** @type {T & { toJSON(): any } } */ ( value ) . toJSON ( ) , cloned ) ;
return clone (
/** @type {T & { toJSON(): any } } */ ( value ) . toJSON ( ) ,
cloned ,
DEV ? ` ${ path } .toJSON() ` : path ,
paths
) ;
}
}
@ -61,10 +98,9 @@ function clone(value, cloned) {
return /** @type {Snapshot<T>} */ ( structuredClone ( value ) ) ;
} catch ( e ) {
if ( DEV ) {
w . state _snapshot _uncloneable ( ) ;
// eslint-disable-next-line no-console
console . warn ( e ) ;
paths . push ( path ) ;
}
return /** @type {Snapshot<T>} */ ( value ) ;
}
}