77
88'use strict' ;
99
10+ const { deleteProperty } = Reflect ;
11+ const isPrimitive = require ( 'is-primitive' ) ;
1012const isPlainObject = require ( 'is-plain-object' ) ;
1113
12- const isObject = val => {
13- return ( typeof val === 'object' && val !== null ) || typeof val === 'function' ;
14+ const isObject = value => {
15+ return ( typeof value === 'object' && value !== null ) || typeof value === 'function' ;
1416} ;
1517
1618const isUnsafeKey = key => {
1719 return key === '__proto__' || key === 'constructor' || key === 'prototype' ;
1820} ;
1921
2022const validateKey = key => {
21- if ( typeof key !== 'string' && typeof key !== 'number' ) {
22- key = String ( key ) ;
23+ if ( ! isPrimitive ( key ) ) {
24+ throw new TypeError ( 'Object keys must be strings or symbols' ) ;
2325 }
2426
2527 if ( isUnsafeKey ( key ) ) {
2628 throw new Error ( `Cannot set unsafe key: "${ key } "` ) ;
2729 }
2830} ;
2931
30- const toString = input => {
32+ const toStringKey = input => {
3133 return Array . isArray ( input ) ? input . flat ( ) . map ( String ) . join ( ',' ) : input ;
3234} ;
3335
@@ -43,71 +45,74 @@ const createMemoKey = (input, options) => {
4345} ;
4446
4547const memoize = ( input , options , fn ) => {
46- const key = toString ( options ? createMemoKey ( input , options ) : input ) ;
48+ const key = toStringKey ( options ? createMemoKey ( input , options ) : input ) ;
4749 validateKey ( key ) ;
4850
49- const val = setValue . cache . get ( key ) || fn ( ) ;
50- setValue . cache . set ( key , val ) ;
51- return val ;
51+ const value = setValue . cache . get ( key ) || fn ( ) ;
52+ setValue . cache . set ( key , value ) ;
53+ return value ;
5254} ;
5355
54- const isNumber = value => {
55- if ( value . trim ( ) !== '' ) {
56- const number = Number ( value ) ;
57- return { is : Number . isInteger ( number ) , number } ;
58- }
59- return { is : false } ;
60- } ;
61-
62- const splitString = ( input , options ) => {
63- const opts = options || { } ;
64- const sep = opts . separator || '.' ;
65- const preserve = sep === '/' ? false : opts . preservePaths ;
56+ const splitString = ( input , options = { } ) => {
57+ const sep = options . separator || '.' ;
58+ const preserve = sep === '/' ? false : options . preservePaths ;
6659
67- if ( typeof input === 'symbol' ) {
60+ if ( typeof input === 'string' && preserve !== false && / \/ / . test ( input ) ) {
6861 return [ input ] ;
6962 }
7063
71- if ( typeof opts . split === 'function' ) {
72- return opts . split ( input ) ;
73- }
64+ const parts = [ ] ;
65+ let part = '' ;
7466
75- const keys = Array . isArray ( input ) ? input : input . split ( sep ) ;
76-
77- if ( typeof input === 'string' && preserve !== false && / \/ / . test ( input ) ) {
78- return [ input ] ;
79- }
67+ const push = part => {
68+ let number ;
69+ if ( part . trim ( ) !== '' && Number . isInteger ( ( number = Number ( part ) ) ) ) {
70+ parts . push ( number ) ;
71+ } else {
72+ parts . push ( part ) ;
73+ }
74+ } ;
8075
81- for ( let i = 0 ; i < keys . length ; i ++ ) {
82- if ( typeof keys [ i ] !== 'string' ) break ;
83- const { is, number } = isNumber ( keys [ i ] ) ;
76+ for ( let i = 0 ; i < input . length ; i ++ ) {
77+ const value = input [ i ] ;
8478
85- if ( is ) {
86- keys [ i ] = number ;
79+ if ( value === '\\' ) {
80+ part += input [ ++ i ] ;
8781 continue ;
8882 }
8983
90- while ( keys [ i ] && i < keys . length && keys [ i ] . endsWith ( '\\' ) && typeof keys [ i + 1 ] === 'string' ) {
91- keys [ i ] = keys [ i ] . slice ( 0 , - 1 ) + sep + keys . splice ( i + 1 , 1 ) ;
84+ if ( value === sep ) {
85+ push ( part ) ;
86+ part = '' ;
87+ continue ;
9288 }
89+
90+ part += value ;
91+ }
92+
93+ if ( part ) {
94+ push ( part ) ;
9395 }
9496
95- return keys ;
97+ return parts ;
9698} ;
9799
98100const split = ( input , options ) => {
101+ if ( options && typeof options . split === 'function' ) return options . split ( input ) ;
102+ if ( typeof input === 'symbol' ) return [ input ] ;
103+ if ( Array . isArray ( input ) ) return input ;
99104 return memoize ( input , options , ( ) => splitString ( input , options ) ) ;
100105} ;
101106
102- const setProp = ( obj , prop , value , options ) => {
107+ const assignProp = ( obj , prop , value , options ) => {
103108 validateKey ( prop ) ;
104109
105110 // Delete property when "value" is undefined
106111 if ( value === undefined ) {
107- delete obj [ prop ] ;
112+ deleteProperty ( obj , prop ) ;
108113
109114 } else if ( options && options . merge ) {
110- const merge = options . merge === true ? Object . assign : options . merge ;
115+ const merge = options . merge === 'function' ? options . merge : Object . assign ;
111116
112117 // Only merge plain objects
113118 if ( merge && isPlainObject ( obj [ prop ] ) && isPlainObject ( value ) ) {
@@ -123,28 +128,25 @@ const setProp = (obj, prop, value, options) => {
123128 return obj ;
124129} ;
125130
126- const setValue = ( obj , path , value , options ) => {
127- if ( ! path ) return obj ;
128- if ( ! isObject ( obj ) ) return obj ;
131+ const setValue = ( target , path , value , options ) => {
132+ if ( ! path || ! isObject ( target ) ) return target ;
129133
130134 const keys = split ( path , options ) ;
131- const len = keys . length ;
132- const target = obj ;
135+ let obj = target ;
133136
134- for ( let i = 0 ; i < len ; i ++ ) {
137+ for ( let i = 0 ; i < keys . length ; i ++ ) {
135138 const key = keys [ i ] ;
136139 const next = keys [ i + 1 ] ;
137140
138141 validateKey ( key ) ;
139142
140143 if ( next === undefined ) {
141- setProp ( obj , key , value , options ) ;
144+ assignProp ( obj , key , value , options ) ;
142145 break ;
143146 }
144147
145148 if ( typeof next === 'number' && ! Array . isArray ( obj [ key ] ) ) {
146- obj [ key ] = [ ] ;
147- obj = obj [ key ] ;
149+ obj = obj [ key ] = [ ] ;
148150 continue ;
149151 }
150152
@@ -158,6 +160,7 @@ const setValue = (obj, path, value, options) => {
158160 return target ;
159161} ;
160162
163+ setValue . split = split ;
161164setValue . cache = new Map ( ) ;
162165setValue . clear = ( ) => {
163166 setValue . cache = new Map ( ) ;
0 commit comments