2022-10-21 14:44:44 +00:00
import { warn } from '../log.js' ;
import { createStringifyContext } from '../stringify/stringify.js' ;
2024-01-17 09:08:10 +00:00
import { isAlias , isSeq , isScalar , isMap , isNode } from './identity.js' ;
2022-10-21 14:44:44 +00:00
import { Scalar } from './Scalar.js' ;
import { toJS } from './toJS.js' ;
const MERGE _KEY = '<<' ;
function addPairToJSMap ( ctx , map , { key , value } ) {
if ( ctx ? . doc . schema . merge && isMergeKey ( key ) ) {
value = isAlias ( value ) ? value . resolve ( ctx . doc ) : value ;
if ( isSeq ( value ) )
for ( const it of value . items )
mergeToJSMap ( ctx , map , it ) ;
else if ( Array . isArray ( value ) )
for ( const it of value )
mergeToJSMap ( ctx , map , it ) ;
else
mergeToJSMap ( ctx , map , value ) ;
}
else {
const jsKey = toJS ( key , '' , ctx ) ;
if ( map instanceof Map ) {
map . set ( jsKey , toJS ( value , jsKey , ctx ) ) ;
}
else if ( map instanceof Set ) {
map . add ( jsKey ) ;
}
else {
const stringKey = stringifyKey ( key , jsKey , ctx ) ;
const jsValue = toJS ( value , stringKey , ctx ) ;
if ( stringKey in map )
Object . defineProperty ( map , stringKey , {
value : jsValue ,
writable : true ,
enumerable : true ,
configurable : true
} ) ;
else
map [ stringKey ] = jsValue ;
}
}
return map ;
}
const isMergeKey = ( key ) => key === MERGE _KEY ||
( isScalar ( key ) &&
key . value === MERGE _KEY &&
( ! key . type || key . type === Scalar . PLAIN ) ) ;
// If the value associated with a merge key is a single mapping node, each of
// its key/value pairs is inserted into the current mapping, unless the key
// already exists in it. If the value associated with the merge key is a
// sequence, then this sequence is expected to contain mapping nodes and each
// of these nodes is merged in turn according to its order in the sequence.
// Keys in mapping nodes earlier in the sequence override keys specified in
// later mapping nodes. -- http://yaml.org/type/merge.html
function mergeToJSMap ( ctx , map , value ) {
const source = ctx && isAlias ( value ) ? value . resolve ( ctx . doc ) : value ;
if ( ! isMap ( source ) )
throw new Error ( 'Merge sources must be maps or map aliases' ) ;
const srcMap = source . toJSON ( null , ctx , Map ) ;
for ( const [ key , value ] of srcMap ) {
if ( map instanceof Map ) {
if ( ! map . has ( key ) )
map . set ( key , value ) ;
}
else if ( map instanceof Set ) {
map . add ( key ) ;
}
else if ( ! Object . prototype . hasOwnProperty . call ( map , key ) ) {
Object . defineProperty ( map , key , {
value ,
writable : true ,
enumerable : true ,
configurable : true
} ) ;
}
}
return map ;
}
function stringifyKey ( key , jsKey , ctx ) {
if ( jsKey === null )
return '' ;
if ( typeof jsKey !== 'object' )
return String ( jsKey ) ;
2024-01-17 09:08:10 +00:00
if ( isNode ( key ) && ctx ? . doc ) {
2022-10-21 14:44:44 +00:00
const strCtx = createStringifyContext ( ctx . doc , { } ) ;
strCtx . anchors = new Set ( ) ;
for ( const node of ctx . anchors . keys ( ) )
strCtx . anchors . add ( node . anchor ) ;
strCtx . inFlow = true ;
strCtx . inStringifyKey = true ;
const strKey = key . toString ( strCtx ) ;
if ( ! ctx . mapKeyWarned ) {
let jsonStr = JSON . stringify ( strKey ) ;
if ( jsonStr . length > 40 )
jsonStr = jsonStr . substring ( 0 , 36 ) + '..."' ;
warn ( ctx . doc . options . logLevel , ` Keys with collection values will be stringified due to JS Object restrictions: ${ jsonStr } . Set mapAsMap: true to use object keys. ` ) ;
ctx . mapKeyWarned = true ;
}
return strKey ;
}
return JSON . stringify ( jsKey ) ;
}
export { addPairToJSMap } ;