@@ -3,15 +3,15 @@ import _ from "lodash"
33import type { IGatsbyNode } from "../redux/types"
44import type { GatsbyIterable } from "../datastore/common/iterable"
55
6- type data = IGatsbyNode | GatsbyIterable < IGatsbyNode >
6+ type Data = IGatsbyNode | GatsbyIterable < IGatsbyNode >
7+
8+ type OmitUndefined = ( data : Data ) => Partial < Data >
79
810/**
911 * @param {Object|Array } data
1012 * @returns {Object|Array } data without undefined values
1113 */
12- type omitUndefined = ( data : data ) => Partial < data >
13-
14- const omitUndefined : omitUndefined = data => {
14+ const omitUndefined : OmitUndefined = data => {
1515 const isPlainObject = _ . isPlainObject ( data )
1616 if ( isPlainObject ) {
1717 return _ . pickBy ( data , p => p !== undefined )
@@ -20,12 +20,12 @@ const omitUndefined: omitUndefined = data => {
2020 return ( data as GatsbyIterable < IGatsbyNode > ) . filter ( p => p !== undefined )
2121}
2222
23+ type isTypeSupported = ( data : Data ) => boolean
24+
2325/**
2426 * @param {* } data
25- * @return {boolean }
27+ * @return {boolean } Boolean if type is supported
2628 */
27- type isTypeSupported = ( data : data ) => boolean
28-
2929const isTypeSupported : isTypeSupported = data => {
3030 if ( data === null ) {
3131 return true
@@ -41,42 +41,67 @@ const isTypeSupported: isTypeSupported = data => {
4141 return isSupported
4242}
4343
44+ type sanitizeNode = (
45+ data : Data ,
46+ isNode ?: boolean ,
47+ path ?: Set < unknown >
48+ ) => Data | undefined
49+
4450/**
4551 * Make data serializable
4652 * @param {(Object|Array) } data to sanitize
4753 * @param {boolean } isNode = true
4854 * @param {Set<string> } path = new Set
4955 */
50-
51- type sanitizeNode = (
52- data : data ,
53- isNode ?: boolean ,
54- path ?: Set < unknown >
55- ) => data | undefined
56-
57- const sanitizeNode : sanitizeNode = ( data , isNode = true , path = new Set ( ) ) => {
56+ export const sanitizeNode : sanitizeNode = (
57+ data ,
58+ isNode = true ,
59+ path = new Set ( )
60+ ) => {
5861 const isPlainObject = _ . isPlainObject ( data )
62+ const isArray = _ . isArray ( data )
5963
60- if ( isPlainObject || _ . isArray ( data ) ) {
64+ if ( isPlainObject || isArray ) {
6165 if ( path . has ( data ) ) return data
6266 path . add ( data )
6367
64- const returnData = isPlainObject ? { } : [ ]
68+ const returnData = isPlainObject
69+ ? ( { } as IGatsbyNode )
70+ : ( [ ] as Array < IGatsbyNode > )
6571 let anyFieldChanged = false
66- _ . each ( data , ( o , key ) => {
72+
73+ // _.each is a "Collection" method and thus objects with "length" property are iterated as arrays
74+ const hasLengthProperty = isPlainObject
75+ ? Object . prototype . hasOwnProperty . call ( data , `length` )
76+ : false
77+ let lengthProperty
78+ if ( hasLengthProperty ) {
79+ lengthProperty = ( data as IGatsbyNode ) . length
80+ delete ( data as IGatsbyNode ) . length
81+ }
82+
83+ _ . each ( data , ( value , key ) => {
6784 if ( isNode && key === `internal` ) {
68- returnData [ key ] = o
85+ returnData [ key ] = value
6986 return
7087 }
71- returnData [ key ] = sanitizeNode ( o as data , false , path )
88+ returnData [ key ] = sanitizeNode ( value as Data , false , path )
7289
73- if ( returnData [ key ] !== o ) {
90+ if ( returnData [ key ] !== value ) {
7491 anyFieldChanged = true
7592 }
7693 } )
7794
95+ if ( hasLengthProperty ) {
96+ ; ( data as IGatsbyNode ) . length = lengthProperty
97+ returnData . length = sanitizeNode ( lengthProperty as Data , false , path )
98+ if ( returnData . length !== lengthProperty ) {
99+ anyFieldChanged = true
100+ }
101+ }
102+
78103 if ( anyFieldChanged ) {
79- data = omitUndefined ( returnData as data ) as data
104+ data = omitUndefined ( returnData as Data ) as Data
80105 }
81106
82107 // arrays and plain objects are supported - no need to to sanitize
@@ -89,5 +114,3 @@ const sanitizeNode: sanitizeNode = (data, isNode = true, path = new Set()) => {
89114 return data
90115 }
91116}
92-
93- export default sanitizeNode
0 commit comments