Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F101534224
resolveSeq-d03cb037.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Feb 11, 08:36
Size
59 KB
Mime Type
text/x-c++
Expires
Thu, Feb 13, 08:36 (2 d)
Engine
blob
Format
Raw Data
Handle
24172373
Attached To
rOACCT Open Access Compliance Check Tool (OACCT)
resolveSeq-d03cb037.js
View Options
'use strict'
;
var
PlainValue
=
require
(
'./PlainValue-ec8e588e.js'
);
function
addCommentBefore
(
str
,
indent
,
comment
)
{
if
(
!
comment
)
return
str
;
const
cc
=
comment
.
replace
(
/[\s\S]^/gm
,
`
$
&
$
{
indent
}
#`
);
return
`#
$
{
cc
}
\
n$
{
indent
}
$
{
str
}
`
;
}
function
addComment
(
str
,
indent
,
comment
)
{
return
!
comment
?
str
:
comment
.
indexOf
(
'\n'
)
===
-
1
?
`
$
{
str
}
#
$
{
comment
}
`
:
`
$
{
str
}
\
n
`
+
comment
.
replace
(
/^/gm
,
`
$
{
indent
||
''
}
#`
);
}
class
Node
{}
function
toJSON
(
value
,
arg
,
ctx
)
{
if
(
Array
.
isArray
(
value
))
return
value
.
map
((
v
,
i
)
=>
toJSON
(
v
,
String
(
i
),
ctx
));
if
(
value
&&
typeof
value
.
toJSON
===
'function'
)
{
const
anchor
=
ctx
&&
ctx
.
anchors
&&
ctx
.
anchors
.
get
(
value
);
if
(
anchor
)
ctx
.
onCreate
=
res
=>
{
anchor
.
res
=
res
;
delete
ctx
.
onCreate
;
};
const
res
=
value
.
toJSON
(
arg
,
ctx
);
if
(
anchor
&&
ctx
.
onCreate
)
ctx
.
onCreate
(
res
);
return
res
;
}
if
((
!
ctx
||
!
ctx
.
keep
)
&&
typeof
value
===
'bigint'
)
return
Number
(
value
);
return
value
;
}
class
Scalar
extends
Node
{
constructor
(
value
)
{
super
();
this
.
value
=
value
;
}
toJSON
(
arg
,
ctx
)
{
return
ctx
&&
ctx
.
keep
?
this
.
value
:
toJSON
(
this
.
value
,
arg
,
ctx
);
}
toString
()
{
return
String
(
this
.
value
);
}
}
function
collectionFromPath
(
schema
,
path
,
value
)
{
let
v
=
value
;
for
(
let
i
=
path
.
length
-
1
;
i
>=
0
;
--
i
)
{
const
k
=
path
[
i
];
if
(
Number
.
isInteger
(
k
)
&&
k
>=
0
)
{
const
a
=
[];
a
[
k
]
=
v
;
v
=
a
;
}
else
{
const
o
=
{};
Object
.
defineProperty
(
o
,
k
,
{
value
:
v
,
writable
:
true
,
enumerable
:
true
,
configurable
:
true
});
v
=
o
;
}
}
return
schema
.
createNode
(
v
,
false
);
}
// null, undefined, or an empty non-string iterable (e.g. [])
const
isEmptyPath
=
path
=>
path
==
null
||
typeof
path
===
'object'
&&
path
[
Symbol
.
iterator
]().
next
().
done
;
class
Collection
extends
Node
{
constructor
(
schema
)
{
super
();
PlainValue
.
_defineProperty
(
this
,
"items"
,
[]);
this
.
schema
=
schema
;
}
addIn
(
path
,
value
)
{
if
(
isEmptyPath
(
path
))
this
.
add
(
value
);
else
{
const
[
key
,
...
rest
]
=
path
;
const
node
=
this
.
get
(
key
,
true
);
if
(
node
instanceof
Collection
)
node
.
addIn
(
rest
,
value
);
else
if
(
node
===
undefined
&&
this
.
schema
)
this
.
set
(
key
,
collectionFromPath
(
this
.
schema
,
rest
,
value
));
else
throw
new
Error
(
`
Expected
YAML
collection
at
$
{
key
}.
Remaining
path
:
$
{
rest
}
`
);
}
}
deleteIn
([
key
,
...
rest
])
{
if
(
rest
.
length
===
0
)
return
this
.
delete
(
key
);
const
node
=
this
.
get
(
key
,
true
);
if
(
node
instanceof
Collection
)
return
node
.
deleteIn
(
rest
);
else
throw
new
Error
(
`
Expected
YAML
collection
at
$
{
key
}.
Remaining
path
:
$
{
rest
}
`
);
}
getIn
([
key
,
...
rest
],
keepScalar
)
{
const
node
=
this
.
get
(
key
,
true
);
if
(
rest
.
length
===
0
)
return
!
keepScalar
&&
node
instanceof
Scalar
?
node
.
value
:
node
;
else
return
node
instanceof
Collection
?
node
.
getIn
(
rest
,
keepScalar
)
:
undefined
;
}
hasAllNullValues
()
{
return
this
.
items
.
every
(
node
=>
{
if
(
!
node
||
node
.
type
!==
'PAIR'
)
return
false
;
const
n
=
node
.
value
;
return
n
==
null
||
n
instanceof
Scalar
&&
n
.
value
==
null
&&
!
n
.
commentBefore
&&
!
n
.
comment
&&
!
n
.
tag
;
});
}
hasIn
([
key
,
...
rest
])
{
if
(
rest
.
length
===
0
)
return
this
.
has
(
key
);
const
node
=
this
.
get
(
key
,
true
);
return
node
instanceof
Collection
?
node
.
hasIn
(
rest
)
:
false
;
}
setIn
([
key
,
...
rest
],
value
)
{
if
(
rest
.
length
===
0
)
{
this
.
set
(
key
,
value
);
}
else
{
const
node
=
this
.
get
(
key
,
true
);
if
(
node
instanceof
Collection
)
node
.
setIn
(
rest
,
value
);
else
if
(
node
===
undefined
&&
this
.
schema
)
this
.
set
(
key
,
collectionFromPath
(
this
.
schema
,
rest
,
value
));
else
throw
new
Error
(
`
Expected
YAML
collection
at
$
{
key
}.
Remaining
path
:
$
{
rest
}
`
);
}
}
// overridden in implementations
/* istanbul ignore next */
toJSON
()
{
return
null
;
}
toString
(
ctx
,
{
blockItem
,
flowChars
,
isMap
,
itemIndent
},
onComment
,
onChompKeep
)
{
const
{
indent
,
indentStep
,
stringify
}
=
ctx
;
const
inFlow
=
this
.
type
===
PlainValue
.
Type
.
FLOW_MAP
||
this
.
type
===
PlainValue
.
Type
.
FLOW_SEQ
||
ctx
.
inFlow
;
if
(
inFlow
)
itemIndent
+=
indentStep
;
const
allNullValues
=
isMap
&&
this
.
hasAllNullValues
();
ctx
=
Object
.
assign
({},
ctx
,
{
allNullValues
,
indent
:
itemIndent
,
inFlow
,
type
:
null
});
let
chompKeep
=
false
;
let
hasItemWithNewLine
=
false
;
const
nodes
=
this
.
items
.
reduce
((
nodes
,
item
,
i
)
=>
{
let
comment
;
if
(
item
)
{
if
(
!
chompKeep
&&
item
.
spaceBefore
)
nodes
.
push
({
type
:
'comment'
,
str
:
''
});
if
(
item
.
commentBefore
)
item
.
commentBefore
.
match
(
/^.*$/gm
).
forEach
(
line
=>
{
nodes
.
push
({
type
:
'comment'
,
str
:
`#
$
{
line
}
`
});
});
if
(
item
.
comment
)
comment
=
item
.
comment
;
if
(
inFlow
&&
(
!
chompKeep
&&
item
.
spaceBefore
||
item
.
commentBefore
||
item
.
comment
||
item
.
key
&&
(
item
.
key
.
commentBefore
||
item
.
key
.
comment
)
||
item
.
value
&&
(
item
.
value
.
commentBefore
||
item
.
value
.
comment
)))
hasItemWithNewLine
=
true
;
}
chompKeep
=
false
;
let
str
=
stringify
(
item
,
ctx
,
()
=>
comment
=
null
,
()
=>
chompKeep
=
true
);
if
(
inFlow
&&
!
hasItemWithNewLine
&&
str
.
includes
(
'\n'
))
hasItemWithNewLine
=
true
;
if
(
inFlow
&&
i
<
this
.
items
.
length
-
1
)
str
+=
','
;
str
=
addComment
(
str
,
itemIndent
,
comment
);
if
(
chompKeep
&&
(
comment
||
inFlow
))
chompKeep
=
false
;
nodes
.
push
({
type
:
'item'
,
str
});
return
nodes
;
},
[]);
let
str
;
if
(
nodes
.
length
===
0
)
{
str
=
flowChars
.
start
+
flowChars
.
end
;
}
else
if
(
inFlow
)
{
const
{
start
,
end
}
=
flowChars
;
const
strings
=
nodes
.
map
(
n
=>
n
.
str
);
if
(
hasItemWithNewLine
||
strings
.
reduce
((
sum
,
str
)
=>
sum
+
str
.
length
+
2
,
2
)
>
Collection
.
maxFlowStringSingleLineLength
)
{
str
=
start
;
for
(
const
s
of
strings
)
{
str
+=
s
?
`\
n$
{
indentStep
}
$
{
indent
}
$
{
s
}
`
:
'\n'
;
}
str
+=
`\
n$
{
indent
}
$
{
end
}
`
;
}
else
{
str
=
`
$
{
start
}
$
{
strings
.
join
(
' '
)}
$
{
end
}
`
;
}
}
else
{
const
strings
=
nodes
.
map
(
blockItem
);
str
=
strings
.
shift
();
for
(
const
s
of
strings
)
str
+=
s
?
`\
n$
{
indent
}
$
{
s
}
`
:
'\n'
;
}
if
(
this
.
comment
)
{
str
+=
'\n'
+
this
.
comment
.
replace
(
/^/gm
,
`
$
{
indent
}
#`
);
if
(
onComment
)
onComment
();
}
else
if
(
chompKeep
&&
onChompKeep
)
onChompKeep
();
return
str
;
}
}
PlainValue
.
_defineProperty
(
Collection
,
"maxFlowStringSingleLineLength"
,
60
);
function
asItemIndex
(
key
)
{
let
idx
=
key
instanceof
Scalar
?
key
.
value
:
key
;
if
(
idx
&&
typeof
idx
===
'string'
)
idx
=
Number
(
idx
);
return
Number
.
isInteger
(
idx
)
&&
idx
>=
0
?
idx
:
null
;
}
class
YAMLSeq
extends
Collection
{
add
(
value
)
{
this
.
items
.
push
(
value
);
}
delete
(
key
)
{
const
idx
=
asItemIndex
(
key
);
if
(
typeof
idx
!==
'number'
)
return
false
;
const
del
=
this
.
items
.
splice
(
idx
,
1
);
return
del
.
length
>
0
;
}
get
(
key
,
keepScalar
)
{
const
idx
=
asItemIndex
(
key
);
if
(
typeof
idx
!==
'number'
)
return
undefined
;
const
it
=
this
.
items
[
idx
];
return
!
keepScalar
&&
it
instanceof
Scalar
?
it
.
value
:
it
;
}
has
(
key
)
{
const
idx
=
asItemIndex
(
key
);
return
typeof
idx
===
'number'
&&
idx
<
this
.
items
.
length
;
}
set
(
key
,
value
)
{
const
idx
=
asItemIndex
(
key
);
if
(
typeof
idx
!==
'number'
)
throw
new
Error
(
`
Expected
a
valid
index
,
not
$
{
key
}.
`
);
this
.
items
[
idx
]
=
value
;
}
toJSON
(
_
,
ctx
)
{
const
seq
=
[];
if
(
ctx
&&
ctx
.
onCreate
)
ctx
.
onCreate
(
seq
);
let
i
=
0
;
for
(
const
item
of
this
.
items
)
seq
.
push
(
toJSON
(
item
,
String
(
i
++
),
ctx
));
return
seq
;
}
toString
(
ctx
,
onComment
,
onChompKeep
)
{
if
(
!
ctx
)
return
JSON
.
stringify
(
this
);
return
super
.
toString
(
ctx
,
{
blockItem
:
n
=>
n
.
type
===
'comment'
?
n
.
str
:
`
-
$
{
n
.
str
}
`
,
flowChars
:
{
start
:
'['
,
end
:
']'
},
isMap
:
false
,
itemIndent
:
(
ctx
.
indent
||
''
)
+
' '
},
onComment
,
onChompKeep
);
}
}
const
stringifyKey
=
(
key
,
jsKey
,
ctx
)
=>
{
if
(
jsKey
===
null
)
return
''
;
if
(
typeof
jsKey
!==
'object'
)
return
String
(
jsKey
);
if
(
key
instanceof
Node
&&
ctx
&&
ctx
.
doc
)
return
key
.
toString
({
anchors
:
Object
.
create
(
null
),
doc
:
ctx
.
doc
,
indent
:
''
,
indentStep
:
ctx
.
indentStep
,
inFlow
:
true
,
inStringifyKey
:
true
,
stringify
:
ctx
.
stringify
});
return
JSON
.
stringify
(
jsKey
);
};
class
Pair
extends
Node
{
constructor
(
key
,
value
=
null
)
{
super
();
this
.
key
=
key
;
this
.
value
=
value
;
this
.
type
=
Pair
.
Type
.
PAIR
;
}
get
commentBefore
()
{
return
this
.
key
instanceof
Node
?
this
.
key
.
commentBefore
:
undefined
;
}
set
commentBefore
(
cb
)
{
if
(
this
.
key
==
null
)
this
.
key
=
new
Scalar
(
null
);
if
(
this
.
key
instanceof
Node
)
this
.
key
.
commentBefore
=
cb
;
else
{
const
msg
=
'Pair.commentBefore is an alias for Pair.key.commentBefore. To set it, the key must be a Node.'
;
throw
new
Error
(
msg
);
}
}
addToJSMap
(
ctx
,
map
)
{
const
key
=
toJSON
(
this
.
key
,
''
,
ctx
);
if
(
map
instanceof
Map
)
{
const
value
=
toJSON
(
this
.
value
,
key
,
ctx
);
map
.
set
(
key
,
value
);
}
else
if
(
map
instanceof
Set
)
{
map
.
add
(
key
);
}
else
{
const
stringKey
=
stringifyKey
(
this
.
key
,
key
,
ctx
);
const
value
=
toJSON
(
this
.
value
,
stringKey
,
ctx
);
if
(
stringKey
in
map
)
Object
.
defineProperty
(
map
,
stringKey
,
{
value
,
writable
:
true
,
enumerable
:
true
,
configurable
:
true
});
else
map
[
stringKey
]
=
value
;
}
return
map
;
}
toJSON
(
_
,
ctx
)
{
const
pair
=
ctx
&&
ctx
.
mapAsMap
?
new
Map
()
:
{};
return
this
.
addToJSMap
(
ctx
,
pair
);
}
toString
(
ctx
,
onComment
,
onChompKeep
)
{
if
(
!
ctx
||
!
ctx
.
doc
)
return
JSON
.
stringify
(
this
);
const
{
indent
:
indentSize
,
indentSeq
,
simpleKeys
}
=
ctx
.
doc
.
options
;
let
{
key
,
value
}
=
this
;
let
keyComment
=
key
instanceof
Node
&&
key
.
comment
;
if
(
simpleKeys
)
{
if
(
keyComment
)
{
throw
new
Error
(
'With simple keys, key nodes cannot have comments'
);
}
if
(
key
instanceof
Collection
)
{
const
msg
=
'With simple keys, collection cannot be used as a key value'
;
throw
new
Error
(
msg
);
}
}
let
explicitKey
=
!
simpleKeys
&&
(
!
key
||
keyComment
||
(
key
instanceof
Node
?
key
instanceof
Collection
||
key
.
type
===
PlainValue
.
Type
.
BLOCK_FOLDED
||
key
.
type
===
PlainValue
.
Type
.
BLOCK_LITERAL
:
typeof
key
===
'object'
));
const
{
doc
,
indent
,
indentStep
,
stringify
}
=
ctx
;
ctx
=
Object
.
assign
({},
ctx
,
{
implicitKey
:
!
explicitKey
,
indent
:
indent
+
indentStep
});
let
chompKeep
=
false
;
let
str
=
stringify
(
key
,
ctx
,
()
=>
keyComment
=
null
,
()
=>
chompKeep
=
true
);
str
=
addComment
(
str
,
ctx
.
indent
,
keyComment
);
if
(
!
explicitKey
&&
str
.
length
>
1024
)
{
if
(
simpleKeys
)
throw
new
Error
(
'With simple keys, single line scalar must not span more than 1024 characters'
);
explicitKey
=
true
;
}
if
(
ctx
.
allNullValues
&&
!
simpleKeys
)
{
if
(
this
.
comment
)
{
str
=
addComment
(
str
,
ctx
.
indent
,
this
.
comment
);
if
(
onComment
)
onComment
();
}
else
if
(
chompKeep
&&
!
keyComment
&&
onChompKeep
)
onChompKeep
();
return
ctx
.
inFlow
&&
!
explicitKey
?
str
:
`
?
$
{
str
}
`
;
}
str
=
explicitKey
?
`
?
$
{
str
}
\
n$
{
indent
}
:
`
:
`
$
{
str
}
:
`
;
if
(
this
.
comment
)
{
// expected (but not strictly required) to be a single-line comment
str
=
addComment
(
str
,
ctx
.
indent
,
this
.
comment
);
if
(
onComment
)
onComment
();
}
let
vcb
=
''
;
let
valueComment
=
null
;
if
(
value
instanceof
Node
)
{
if
(
value
.
spaceBefore
)
vcb
=
'\n'
;
if
(
value
.
commentBefore
)
{
const
cs
=
value
.
commentBefore
.
replace
(
/^/gm
,
`
$
{
ctx
.
indent
}
#`
);
vcb
+=
`\
n$
{
cs
}
`
;
}
valueComment
=
value
.
comment
;
}
else
if
(
value
&&
typeof
value
===
'object'
)
{
value
=
doc
.
schema
.
createNode
(
value
,
true
);
}
ctx
.
implicitKey
=
false
;
if
(
!
explicitKey
&&
!
this
.
comment
&&
value
instanceof
Scalar
)
ctx
.
indentAtStart
=
str
.
length
+
1
;
chompKeep
=
false
;
if
(
!
indentSeq
&&
indentSize
>=
2
&&
!
ctx
.
inFlow
&&
!
explicitKey
&&
value
instanceof
YAMLSeq
&&
value
.
type
!==
PlainValue
.
Type
.
FLOW_SEQ
&&
!
value
.
tag
&&
!
doc
.
anchors
.
getName
(
value
))
{
// If indentSeq === false, consider '- ' as part of indentation where possible
ctx
.
indent
=
ctx
.
indent
.
substr
(
2
);
}
const
valueStr
=
stringify
(
value
,
ctx
,
()
=>
valueComment
=
null
,
()
=>
chompKeep
=
true
);
let
ws
=
' '
;
if
(
vcb
||
this
.
comment
)
{
ws
=
`
$
{
vcb
}
\
n$
{
ctx
.
indent
}
`
;
}
else
if
(
!
explicitKey
&&
value
instanceof
Collection
)
{
const
flow
=
valueStr
[
0
]
===
'['
||
valueStr
[
0
]
===
'{'
;
if
(
!
flow
||
valueStr
.
includes
(
'\n'
))
ws
=
`\
n$
{
ctx
.
indent
}
`
;
}
else
if
(
valueStr
[
0
]
===
'\n'
)
ws
=
''
;
if
(
chompKeep
&&
!
valueComment
&&
onChompKeep
)
onChompKeep
();
return
addComment
(
str
+
ws
+
valueStr
,
ctx
.
indent
,
valueComment
);
}
}
PlainValue
.
_defineProperty
(
Pair
,
"Type"
,
{
PAIR
:
'PAIR'
,
MERGE_PAIR
:
'MERGE_PAIR'
});
const
getAliasCount
=
(
node
,
anchors
)
=>
{
if
(
node
instanceof
Alias
)
{
const
anchor
=
anchors
.
get
(
node
.
source
);
return
anchor
.
count
*
anchor
.
aliasCount
;
}
else
if
(
node
instanceof
Collection
)
{
let
count
=
0
;
for
(
const
item
of
node
.
items
)
{
const
c
=
getAliasCount
(
item
,
anchors
);
if
(
c
>
count
)
count
=
c
;
}
return
count
;
}
else
if
(
node
instanceof
Pair
)
{
const
kc
=
getAliasCount
(
node
.
key
,
anchors
);
const
vc
=
getAliasCount
(
node
.
value
,
anchors
);
return
Math
.
max
(
kc
,
vc
);
}
return
1
;
};
class
Alias
extends
Node
{
static
stringify
({
range
,
source
},
{
anchors
,
doc
,
implicitKey
,
inStringifyKey
})
{
let
anchor
=
Object
.
keys
(
anchors
).
find
(
a
=>
anchors
[
a
]
===
source
);
if
(
!
anchor
&&
inStringifyKey
)
anchor
=
doc
.
anchors
.
getName
(
source
)
||
doc
.
anchors
.
newName
();
if
(
anchor
)
return
`
*
$
{
anchor
}
$
{
implicitKey
?
' '
:
''
}
`
;
const
msg
=
doc
.
anchors
.
getName
(
source
)
?
'Alias node must be after source node'
:
'Source node not found for alias node'
;
throw
new
Error
(
`
$
{
msg
}
[
$
{
range
}]
`
);
}
constructor
(
source
)
{
super
();
this
.
source
=
source
;
this
.
type
=
PlainValue
.
Type
.
ALIAS
;
}
set
tag
(
t
)
{
throw
new
Error
(
'Alias nodes cannot have tags'
);
}
toJSON
(
arg
,
ctx
)
{
if
(
!
ctx
)
return
toJSON
(
this
.
source
,
arg
,
ctx
);
const
{
anchors
,
maxAliasCount
}
=
ctx
;
const
anchor
=
anchors
.
get
(
this
.
source
);
/* istanbul ignore if */
if
(
!
anchor
||
anchor
.
res
===
undefined
)
{
const
msg
=
'This should not happen: Alias anchor was not resolved?'
;
if
(
this
.
cstNode
)
throw
new
PlainValue
.
YAMLReferenceError
(
this
.
cstNode
,
msg
);
else
throw
new
ReferenceError
(
msg
);
}
if
(
maxAliasCount
>=
0
)
{
anchor
.
count
+=
1
;
if
(
anchor
.
aliasCount
===
0
)
anchor
.
aliasCount
=
getAliasCount
(
this
.
source
,
anchors
);
if
(
anchor
.
count
*
anchor
.
aliasCount
>
maxAliasCount
)
{
const
msg
=
'Excessive alias count indicates a resource exhaustion attack'
;
if
(
this
.
cstNode
)
throw
new
PlainValue
.
YAMLReferenceError
(
this
.
cstNode
,
msg
);
else
throw
new
ReferenceError
(
msg
);
}
}
return
anchor
.
res
;
}
// Only called when stringifying an alias mapping key while constructing
// Object output.
toString
(
ctx
)
{
return
Alias
.
stringify
(
this
,
ctx
);
}
}
PlainValue
.
_defineProperty
(
Alias
,
"default"
,
true
);
function
findPair
(
items
,
key
)
{
const
k
=
key
instanceof
Scalar
?
key
.
value
:
key
;
for
(
const
it
of
items
)
{
if
(
it
instanceof
Pair
)
{
if
(
it
.
key
===
key
||
it
.
key
===
k
)
return
it
;
if
(
it
.
key
&&
it
.
key
.
value
===
k
)
return
it
;
}
}
return
undefined
;
}
class
YAMLMap
extends
Collection
{
add
(
pair
,
overwrite
)
{
if
(
!
pair
)
pair
=
new
Pair
(
pair
);
else
if
(
!
(
pair
instanceof
Pair
))
pair
=
new
Pair
(
pair
.
key
||
pair
,
pair
.
value
);
const
prev
=
findPair
(
this
.
items
,
pair
.
key
);
const
sortEntries
=
this
.
schema
&&
this
.
schema
.
sortMapEntries
;
if
(
prev
)
{
if
(
overwrite
)
prev
.
value
=
pair
.
value
;
else
throw
new
Error
(
`
Key
$
{
pair
.
key
}
already
set
`
);
}
else
if
(
sortEntries
)
{
const
i
=
this
.
items
.
findIndex
(
item
=>
sortEntries
(
pair
,
item
)
<
0
);
if
(
i
===
-
1
)
this
.
items
.
push
(
pair
);
else
this
.
items
.
splice
(
i
,
0
,
pair
);
}
else
{
this
.
items
.
push
(
pair
);
}
}
delete
(
key
)
{
const
it
=
findPair
(
this
.
items
,
key
);
if
(
!
it
)
return
false
;
const
del
=
this
.
items
.
splice
(
this
.
items
.
indexOf
(
it
),
1
);
return
del
.
length
>
0
;
}
get
(
key
,
keepScalar
)
{
const
it
=
findPair
(
this
.
items
,
key
);
const
node
=
it
&&
it
.
value
;
return
!
keepScalar
&&
node
instanceof
Scalar
?
node
.
value
:
node
;
}
has
(
key
)
{
return
!!
findPair
(
this
.
items
,
key
);
}
set
(
key
,
value
)
{
this
.
add
(
new
Pair
(
key
,
value
),
true
);
}
/**
* @param {*} arg ignored
* @param {*} ctx Conversion context, originally set in Document#toJSON()
* @param {Class} Type If set, forces the returned collection type
* @returns {*} Instance of Type, Map, or Object
*/
toJSON
(
_
,
ctx
,
Type
)
{
const
map
=
Type
?
new
Type
()
:
ctx
&&
ctx
.
mapAsMap
?
new
Map
()
:
{};
if
(
ctx
&&
ctx
.
onCreate
)
ctx
.
onCreate
(
map
);
for
(
const
item
of
this
.
items
)
item
.
addToJSMap
(
ctx
,
map
);
return
map
;
}
toString
(
ctx
,
onComment
,
onChompKeep
)
{
if
(
!
ctx
)
return
JSON
.
stringify
(
this
);
for
(
const
item
of
this
.
items
)
{
if
(
!
(
item
instanceof
Pair
))
throw
new
Error
(
`
Map
items
must
all
be
pairs
;
found
$
{
JSON
.
stringify
(
item
)}
instead
`
);
}
return
super
.
toString
(
ctx
,
{
blockItem
:
n
=>
n
.
str
,
flowChars
:
{
start
:
'{'
,
end
:
'}'
},
isMap
:
true
,
itemIndent
:
ctx
.
indent
||
''
},
onComment
,
onChompKeep
);
}
}
const
MERGE_KEY
=
'<<'
;
class
Merge
extends
Pair
{
constructor
(
pair
)
{
if
(
pair
instanceof
Pair
)
{
let
seq
=
pair
.
value
;
if
(
!
(
seq
instanceof
YAMLSeq
))
{
seq
=
new
YAMLSeq
();
seq
.
items
.
push
(
pair
.
value
);
seq
.
range
=
pair
.
value
.
range
;
}
super
(
pair
.
key
,
seq
);
this
.
range
=
pair
.
range
;
}
else
{
super
(
new
Scalar
(
MERGE_KEY
),
new
YAMLSeq
());
}
this
.
type
=
Pair
.
Type
.
MERGE_PAIR
;
}
// 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
addToJSMap
(
ctx
,
map
)
{
for
(
const
{
source
}
of
this
.
value
.
items
)
{
if
(
!
(
source
instanceof
YAMLMap
))
throw
new
Error
(
'Merge sources must be maps'
);
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
;
}
toString
(
ctx
,
onComment
)
{
const
seq
=
this
.
value
;
if
(
seq
.
items
.
length
>
1
)
return
super
.
toString
(
ctx
,
onComment
);
this
.
value
=
seq
.
items
[
0
];
const
str
=
super
.
toString
(
ctx
,
onComment
);
this
.
value
=
seq
;
return
str
;
}
}
const
binaryOptions
=
{
defaultType
:
PlainValue
.
Type
.
BLOCK_LITERAL
,
lineWidth
:
76
};
const
boolOptions
=
{
trueStr
:
'true'
,
falseStr
:
'false'
};
const
intOptions
=
{
asBigInt
:
false
};
const
nullOptions
=
{
nullStr
:
'null'
};
const
strOptions
=
{
defaultType
:
PlainValue
.
Type
.
PLAIN
,
doubleQuoted
:
{
jsonEncoding
:
false
,
minMultiLineLength
:
40
},
fold
:
{
lineWidth
:
80
,
minContentWidth
:
20
}
};
function
resolveScalar
(
str
,
tags
,
scalarFallback
)
{
for
(
const
{
format
,
test
,
resolve
}
of
tags
)
{
if
(
test
)
{
const
match
=
str
.
match
(
test
);
if
(
match
)
{
let
res
=
resolve
.
apply
(
null
,
match
);
if
(
!
(
res
instanceof
Scalar
))
res
=
new
Scalar
(
res
);
if
(
format
)
res
.
format
=
format
;
return
res
;
}
}
}
if
(
scalarFallback
)
str
=
scalarFallback
(
str
);
return
new
Scalar
(
str
);
}
const
FOLD_FLOW
=
'flow'
;
const
FOLD_BLOCK
=
'block'
;
const
FOLD_QUOTED
=
'quoted'
;
// presumes i+1 is at the start of a line
// returns index of last newline in more-indented block
const
consumeMoreIndentedLines
=
(
text
,
i
)
=>
{
let
ch
=
text
[
i
+
1
];
while
(
ch
===
' '
||
ch
===
'\t'
)
{
do
{
ch
=
text
[
i
+=
1
];
}
while
(
ch
&&
ch
!==
'\n'
);
ch
=
text
[
i
+
1
];
}
return
i
;
};
/**
* Tries to keep input at up to `lineWidth` characters, splitting only on spaces
* not followed by newlines or spaces unless `mode` is `'quoted'`. Lines are
* terminated with `\n` and started with `indent`.
*
* @param {string} text
* @param {string} indent
* @param {string} [mode='flow'] `'block'` prevents more-indented lines
* from being folded; `'quoted'` allows for `\` escapes, including escaped
* newlines
* @param {Object} options
* @param {number} [options.indentAtStart] Accounts for leading contents on
* the first line, defaulting to `indent.length`
* @param {number} [options.lineWidth=80]
* @param {number} [options.minContentWidth=20] Allow highly indented lines to
* stretch the line width or indent content from the start
* @param {function} options.onFold Called once if the text is folded
* @param {function} options.onFold Called once if any line of text exceeds
* lineWidth characters
*/
function
foldFlowLines
(
text
,
indent
,
mode
,
{
indentAtStart
,
lineWidth
=
80
,
minContentWidth
=
20
,
onFold
,
onOverflow
})
{
if
(
!
lineWidth
||
lineWidth
<
0
)
return
text
;
const
endStep
=
Math
.
max
(
1
+
minContentWidth
,
1
+
lineWidth
-
indent
.
length
);
if
(
text
.
length
<=
endStep
)
return
text
;
const
folds
=
[];
const
escapedFolds
=
{};
let
end
=
lineWidth
-
indent
.
length
;
if
(
typeof
indentAtStart
===
'number'
)
{
if
(
indentAtStart
>
lineWidth
-
Math
.
max
(
2
,
minContentWidth
))
folds
.
push
(
0
);
else
end
=
lineWidth
-
indentAtStart
;
}
let
split
=
undefined
;
let
prev
=
undefined
;
let
overflow
=
false
;
let
i
=
-
1
;
let
escStart
=
-
1
;
let
escEnd
=
-
1
;
if
(
mode
===
FOLD_BLOCK
)
{
i
=
consumeMoreIndentedLines
(
text
,
i
);
if
(
i
!==
-
1
)
end
=
i
+
endStep
;
}
for
(
let
ch
;
ch
=
text
[
i
+=
1
];)
{
if
(
mode
===
FOLD_QUOTED
&&
ch
===
'\\'
)
{
escStart
=
i
;
switch
(
text
[
i
+
1
])
{
case
'x'
:
i
+=
3
;
break
;
case
'u'
:
i
+=
5
;
break
;
case
'U'
:
i
+=
9
;
break
;
default
:
i
+=
1
;
}
escEnd
=
i
;
}
if
(
ch
===
'\n'
)
{
if
(
mode
===
FOLD_BLOCK
)
i
=
consumeMoreIndentedLines
(
text
,
i
);
end
=
i
+
endStep
;
split
=
undefined
;
}
else
{
if
(
ch
===
' '
&&
prev
&&
prev
!==
' '
&&
prev
!==
'\n'
&&
prev
!==
'\t'
)
{
// space surrounded by non-space can be replaced with newline + indent
const
next
=
text
[
i
+
1
];
if
(
next
&&
next
!==
' '
&&
next
!==
'\n'
&&
next
!==
'\t'
)
split
=
i
;
}
if
(
i
>=
end
)
{
if
(
split
)
{
folds
.
push
(
split
);
end
=
split
+
endStep
;
split
=
undefined
;
}
else
if
(
mode
===
FOLD_QUOTED
)
{
// white-space collected at end may stretch past lineWidth
while
(
prev
===
' '
||
prev
===
'\t'
)
{
prev
=
ch
;
ch
=
text
[
i
+=
1
];
overflow
=
true
;
}
// Account for newline escape, but don't break preceding escape
const
j
=
i
>
escEnd
+
1
?
i
-
2
:
escStart
-
1
;
// Bail out if lineWidth & minContentWidth are shorter than an escape string
if
(
escapedFolds
[
j
])
return
text
;
folds
.
push
(
j
);
escapedFolds
[
j
]
=
true
;
end
=
j
+
endStep
;
split
=
undefined
;
}
else
{
overflow
=
true
;
}
}
}
prev
=
ch
;
}
if
(
overflow
&&
onOverflow
)
onOverflow
();
if
(
folds
.
length
===
0
)
return
text
;
if
(
onFold
)
onFold
();
let
res
=
text
.
slice
(
0
,
folds
[
0
]);
for
(
let
i
=
0
;
i
<
folds
.
length
;
++
i
)
{
const
fold
=
folds
[
i
];
const
end
=
folds
[
i
+
1
]
||
text
.
length
;
if
(
fold
===
0
)
res
=
`\
n$
{
indent
}
$
{
text
.
slice
(
0
,
end
)}
`
;
else
{
if
(
mode
===
FOLD_QUOTED
&&
escapedFolds
[
fold
])
res
+=
`
$
{
text
[
fold
]}
\\`
;
res
+=
`\
n$
{
indent
}
$
{
text
.
slice
(
fold
+
1
,
end
)}
`
;
}
}
return
res
;
}
const
getFoldOptions
=
({
indentAtStart
})
=>
indentAtStart
?
Object
.
assign
({
indentAtStart
},
strOptions
.
fold
)
:
strOptions
.
fold
;
// Also checks for lines starting with %, as parsing the output as YAML 1.1 will
// presume that's starting a new document.
const
containsDocumentMarker
=
str
=>
/^(%|---|\.\.\.)/m
.
test
(
str
);
function
lineLengthOverLimit
(
str
,
lineWidth
,
indentLength
)
{
if
(
!
lineWidth
||
lineWidth
<
0
)
return
false
;
const
limit
=
lineWidth
-
indentLength
;
const
strLen
=
str
.
length
;
if
(
strLen
<=
limit
)
return
false
;
for
(
let
i
=
0
,
start
=
0
;
i
<
strLen
;
++
i
)
{
if
(
str
[
i
]
===
'\n'
)
{
if
(
i
-
start
>
limit
)
return
true
;
start
=
i
+
1
;
if
(
strLen
-
start
<=
limit
)
return
false
;
}
}
return
true
;
}
function
doubleQuotedString
(
value
,
ctx
)
{
const
{
implicitKey
}
=
ctx
;
const
{
jsonEncoding
,
minMultiLineLength
}
=
strOptions
.
doubleQuoted
;
const
json
=
JSON
.
stringify
(
value
);
if
(
jsonEncoding
)
return
json
;
const
indent
=
ctx
.
indent
||
(
containsDocumentMarker
(
value
)
?
' '
:
''
);
let
str
=
''
;
let
start
=
0
;
for
(
let
i
=
0
,
ch
=
json
[
i
];
ch
;
ch
=
json
[
++
i
])
{
if
(
ch
===
' '
&&
json
[
i
+
1
]
===
'\\'
&&
json
[
i
+
2
]
===
'n'
)
{
// space before newline needs to be escaped to not be folded
str
+=
json
.
slice
(
start
,
i
)
+
'\\ '
;
i
+=
1
;
start
=
i
;
ch
=
'\\'
;
}
if
(
ch
===
'\\'
)
switch
(
json
[
i
+
1
])
{
case
'u'
:
{
str
+=
json
.
slice
(
start
,
i
);
const
code
=
json
.
substr
(
i
+
2
,
4
);
switch
(
code
)
{
case
'0000'
:
str
+=
'\\0'
;
break
;
case
'0007'
:
str
+=
'\\a'
;
break
;
case
'000b'
:
str
+=
'\\v'
;
break
;
case
'001b'
:
str
+=
'\\e'
;
break
;
case
'0085'
:
str
+=
'\\N'
;
break
;
case
'00a0'
:
str
+=
'\\_'
;
break
;
case
'2028'
:
str
+=
'\\L'
;
break
;
case
'2029'
:
str
+=
'\\P'
;
break
;
default
:
if
(
code
.
substr
(
0
,
2
)
===
'00'
)
str
+=
'\\x'
+
code
.
substr
(
2
);
else
str
+=
json
.
substr
(
i
,
6
);
}
i
+=
5
;
start
=
i
+
1
;
}
break
;
case
'n'
:
if
(
implicitKey
||
json
[
i
+
2
]
===
'"'
||
json
.
length
<
minMultiLineLength
)
{
i
+=
1
;
}
else
{
// folding will eat first newline
str
+=
json
.
slice
(
start
,
i
)
+
'\n\n'
;
while
(
json
[
i
+
2
]
===
'\\'
&&
json
[
i
+
3
]
===
'n'
&&
json
[
i
+
4
]
!==
'"'
)
{
str
+=
'\n'
;
i
+=
2
;
}
str
+=
indent
;
// space after newline needs to be escaped to not be folded
if
(
json
[
i
+
2
]
===
' '
)
str
+=
'\\'
;
i
+=
1
;
start
=
i
+
1
;
}
break
;
default
:
i
+=
1
;
}
}
str
=
start
?
str
+
json
.
slice
(
start
)
:
json
;
return
implicitKey
?
str
:
foldFlowLines
(
str
,
indent
,
FOLD_QUOTED
,
getFoldOptions
(
ctx
));
}
function
singleQuotedString
(
value
,
ctx
)
{
if
(
ctx
.
implicitKey
)
{
if
(
/\n/
.
test
(
value
))
return
doubleQuotedString
(
value
,
ctx
);
}
else
{
// single quoted string can't have leading or trailing whitespace around newline
if
(
/[ \t]\n|\n[ \t]/
.
test
(
value
))
return
doubleQuotedString
(
value
,
ctx
);
}
const
indent
=
ctx
.
indent
||
(
containsDocumentMarker
(
value
)
?
' '
:
''
);
const
res
=
"'"
+
value
.
replace
(
/'/g
,
"''"
).
replace
(
/\n+/g
,
`
$
&
\
n$
{
indent
}
`
)
+
"'"
;
return
ctx
.
implicitKey
?
res
:
foldFlowLines
(
res
,
indent
,
FOLD_FLOW
,
getFoldOptions
(
ctx
));
}
function
blockString
({
comment
,
type
,
value
},
ctx
,
onComment
,
onChompKeep
)
{
// 1. Block can't end in whitespace unless the last line is non-empty.
// 2. Strings consisting of only whitespace are best rendered explicitly.
if
(
/\n[\t ]+$/
.
test
(
value
)
||
/^\s*$/
.
test
(
value
))
{
return
doubleQuotedString
(
value
,
ctx
);
}
const
indent
=
ctx
.
indent
||
(
ctx
.
forceBlockIndent
||
containsDocumentMarker
(
value
)
?
' '
:
''
);
const
indentSize
=
indent
?
'2'
:
'1'
;
// root is at -1
const
literal
=
type
===
PlainValue
.
Type
.
BLOCK_FOLDED
?
false
:
type
===
PlainValue
.
Type
.
BLOCK_LITERAL
?
true
:
!
lineLengthOverLimit
(
value
,
strOptions
.
fold
.
lineWidth
,
indent
.
length
);
let
header
=
literal
?
'|'
:
'>'
;
if
(
!
value
)
return
header
+
'\n'
;
let
wsStart
=
''
;
let
wsEnd
=
''
;
value
=
value
.
replace
(
/[\n\t ]*$/
,
ws
=>
{
const
n
=
ws
.
indexOf
(
'\n'
);
if
(
n
===
-
1
)
{
header
+=
'-'
;
// strip
}
else
if
(
value
===
ws
||
n
!==
ws
.
length
-
1
)
{
header
+=
'+'
;
// keep
if
(
onChompKeep
)
onChompKeep
();
}
wsEnd
=
ws
.
replace
(
/\n$/
,
''
);
return
''
;
}).
replace
(
/^[\n ]*/
,
ws
=>
{
if
(
ws
.
indexOf
(
' '
)
!==
-
1
)
header
+=
indentSize
;
const
m
=
ws
.
match
(
/ +$/
);
if
(
m
)
{
wsStart
=
ws
.
slice
(
0
,
-
m
[
0
].
length
);
return
m
[
0
];
}
else
{
wsStart
=
ws
;
return
''
;
}
});
if
(
wsEnd
)
wsEnd
=
wsEnd
.
replace
(
/\n+(?!\n|$)/g
,
`
$
&
$
{
indent
}
`
);
if
(
wsStart
)
wsStart
=
wsStart
.
replace
(
/\n+/g
,
`
$
&
$
{
indent
}
`
);
if
(
comment
)
{
header
+=
' #'
+
comment
.
replace
(
/ ?[\r\n]+/g
,
' '
);
if
(
onComment
)
onComment
();
}
if
(
!
value
)
return
`
$
{
header
}
$
{
indentSize
}
\
n$
{
indent
}
$
{
wsEnd
}
`
;
if
(
literal
)
{
value
=
value
.
replace
(
/\n+/g
,
`
$
&
$
{
indent
}
`
);
return
`
$
{
header
}
\
n$
{
indent
}
$
{
wsStart
}
$
{
value
}
$
{
wsEnd
}
`
;
}
value
=
value
.
replace
(
/\n+/g
,
'\n$&'
).
replace
(
/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g
,
'$1$2'
)
// more-indented lines aren't folded
// ^ ind.line ^ empty ^ capture next empty lines only at end of indent
.
replace
(
/\n+/g
,
`
$
&
$
{
indent
}
`
);
const
body
=
foldFlowLines
(
`
$
{
wsStart
}
$
{
value
}
$
{
wsEnd
}
`
,
indent
,
FOLD_BLOCK
,
strOptions
.
fold
);
return
`
$
{
header
}
\
n$
{
indent
}
$
{
body
}
`
;
}
function
plainString
(
item
,
ctx
,
onComment
,
onChompKeep
)
{
const
{
comment
,
type
,
value
}
=
item
;
const
{
actualString
,
implicitKey
,
indent
,
inFlow
}
=
ctx
;
if
(
implicitKey
&&
/[\n[\]{},]/
.
test
(
value
)
||
inFlow
&&
/[[\]{},]/
.
test
(
value
))
{
return
doubleQuotedString
(
value
,
ctx
);
}
if
(
!
value
||
/^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/
.
test
(
value
))
{
// not allowed:
// - empty string, '-' or '?'
// - start with an indicator character (except [?:-]) or /[?-] /
// - '\n ', ': ' or ' \n' anywhere
// - '#' not preceded by a non-space char
// - end with ' ' or ':'
return
implicitKey
||
inFlow
||
value
.
indexOf
(
'\n'
)
===
-
1
?
value
.
indexOf
(
'"'
)
!==
-
1
&&
value
.
indexOf
(
"'"
)
===
-
1
?
singleQuotedString
(
value
,
ctx
)
:
doubleQuotedString
(
value
,
ctx
)
:
blockString
(
item
,
ctx
,
onComment
,
onChompKeep
);
}
if
(
!
implicitKey
&&
!
inFlow
&&
type
!==
PlainValue
.
Type
.
PLAIN
&&
value
.
indexOf
(
'\n'
)
!==
-
1
)
{
// Where allowed & type not set explicitly, prefer block style for multiline strings
return
blockString
(
item
,
ctx
,
onComment
,
onChompKeep
);
}
if
(
indent
===
''
&&
containsDocumentMarker
(
value
))
{
ctx
.
forceBlockIndent
=
true
;
return
blockString
(
item
,
ctx
,
onComment
,
onChompKeep
);
}
const
str
=
value
.
replace
(
/\n+/g
,
`
$
&
\
n$
{
indent
}
`
);
// Verify that output will be parsed as a string, as e.g. plain numbers and
// booleans get parsed with those types in v1.2 (e.g. '42', 'true' & '0.9e-3'),
// and others in v1.1.
if
(
actualString
)
{
const
{
tags
}
=
ctx
.
doc
.
schema
;
const
resolved
=
resolveScalar
(
str
,
tags
,
tags
.
scalarFallback
).
value
;
if
(
typeof
resolved
!==
'string'
)
return
doubleQuotedString
(
value
,
ctx
);
}
const
body
=
implicitKey
?
str
:
foldFlowLines
(
str
,
indent
,
FOLD_FLOW
,
getFoldOptions
(
ctx
));
if
(
comment
&&
!
inFlow
&&
(
body
.
indexOf
(
'\n'
)
!==
-
1
||
comment
.
indexOf
(
'\n'
)
!==
-
1
))
{
if
(
onComment
)
onComment
();
return
addCommentBefore
(
body
,
indent
,
comment
);
}
return
body
;
}
function
stringifyString
(
item
,
ctx
,
onComment
,
onChompKeep
)
{
const
{
defaultType
}
=
strOptions
;
const
{
implicitKey
,
inFlow
}
=
ctx
;
let
{
type
,
value
}
=
item
;
if
(
typeof
value
!==
'string'
)
{
value
=
String
(
value
);
item
=
Object
.
assign
({},
item
,
{
value
});
}
const
_stringify
=
_type
=>
{
switch
(
_type
)
{
case
PlainValue
.
Type
.
BLOCK_FOLDED
:
case
PlainValue
.
Type
.
BLOCK_LITERAL
:
return
blockString
(
item
,
ctx
,
onComment
,
onChompKeep
);
case
PlainValue
.
Type
.
QUOTE_DOUBLE
:
return
doubleQuotedString
(
value
,
ctx
);
case
PlainValue
.
Type
.
QUOTE_SINGLE
:
return
singleQuotedString
(
value
,
ctx
);
case
PlainValue
.
Type
.
PLAIN
:
return
plainString
(
item
,
ctx
,
onComment
,
onChompKeep
);
default
:
return
null
;
}
};
if
(
type
!==
PlainValue
.
Type
.
QUOTE_DOUBLE
&&
/[\x00-\x08\x0b-\x1f\x7f-\x9f]/
.
test
(
value
))
{
// force double quotes on control characters
type
=
PlainValue
.
Type
.
QUOTE_DOUBLE
;
}
else
if
((
implicitKey
||
inFlow
)
&&
(
type
===
PlainValue
.
Type
.
BLOCK_FOLDED
||
type
===
PlainValue
.
Type
.
BLOCK_LITERAL
))
{
// should not happen; blocks are not valid inside flow containers
type
=
PlainValue
.
Type
.
QUOTE_DOUBLE
;
}
let
res
=
_stringify
(
type
);
if
(
res
===
null
)
{
res
=
_stringify
(
defaultType
);
if
(
res
===
null
)
throw
new
Error
(
`
Unsupported
default
string
type
$
{
defaultType
}
`
);
}
return
res
;
}
function
stringifyNumber
({
format
,
minFractionDigits
,
tag
,
value
})
{
if
(
typeof
value
===
'bigint'
)
return
String
(
value
);
if
(
!
isFinite
(
value
))
return
isNaN
(
value
)
?
'.nan'
:
value
<
0
?
'-.inf'
:
'.inf'
;
let
n
=
JSON
.
stringify
(
value
);
if
(
!
format
&&
minFractionDigits
&&
(
!
tag
||
tag
===
'tag:yaml.org,2002:float'
)
&&
/^\d/
.
test
(
n
))
{
let
i
=
n
.
indexOf
(
'.'
);
if
(
i
<
0
)
{
i
=
n
.
length
;
n
+=
'.'
;
}
let
d
=
minFractionDigits
-
(
n
.
length
-
i
-
1
);
while
(
d
--
>
0
)
n
+=
'0'
;
}
return
n
;
}
function
checkFlowCollectionEnd
(
errors
,
cst
)
{
let
char
,
name
;
switch
(
cst
.
type
)
{
case
PlainValue
.
Type
.
FLOW_MAP
:
char
=
'}'
;
name
=
'flow map'
;
break
;
case
PlainValue
.
Type
.
FLOW_SEQ
:
char
=
']'
;
name
=
'flow sequence'
;
break
;
default
:
errors
.
push
(
new
PlainValue
.
YAMLSemanticError
(
cst
,
'Not a flow collection!?'
));
return
;
}
let
lastItem
;
for
(
let
i
=
cst
.
items
.
length
-
1
;
i
>=
0
;
--
i
)
{
const
item
=
cst
.
items
[
i
];
if
(
!
item
||
item
.
type
!==
PlainValue
.
Type
.
COMMENT
)
{
lastItem
=
item
;
break
;
}
}
if
(
lastItem
&&
lastItem
.
char
!==
char
)
{
const
msg
=
`
Expected
$
{
name
}
to
end
with
$
{
char
}
`
;
let
err
;
if
(
typeof
lastItem
.
offset
===
'number'
)
{
err
=
new
PlainValue
.
YAMLSemanticError
(
cst
,
msg
);
err
.
offset
=
lastItem
.
offset
+
1
;
}
else
{
err
=
new
PlainValue
.
YAMLSemanticError
(
lastItem
,
msg
);
if
(
lastItem
.
range
&&
lastItem
.
range
.
end
)
err
.
offset
=
lastItem
.
range
.
end
-
lastItem
.
range
.
start
;
}
errors
.
push
(
err
);
}
}
function
checkFlowCommentSpace
(
errors
,
comment
)
{
const
prev
=
comment
.
context
.
src
[
comment
.
range
.
start
-
1
];
if
(
prev
!==
'\n'
&&
prev
!==
'\t'
&&
prev
!==
' '
)
{
const
msg
=
'Comments must be separated from other tokens by white space characters'
;
errors
.
push
(
new
PlainValue
.
YAMLSemanticError
(
comment
,
msg
));
}
}
function
getLongKeyError
(
source
,
key
)
{
const
sk
=
String
(
key
);
const
k
=
sk
.
substr
(
0
,
8
)
+
'...'
+
sk
.
substr
(
-
8
);
return
new
PlainValue
.
YAMLSemanticError
(
source
,
`
The
"${k}"
key
is
too
long
`
);
}
function
resolveComments
(
collection
,
comments
)
{
for
(
const
{
afterKey
,
before
,
comment
}
of
comments
)
{
let
item
=
collection
.
items
[
before
];
if
(
!
item
)
{
if
(
comment
!==
undefined
)
{
if
(
collection
.
comment
)
collection
.
comment
+=
'\n'
+
comment
;
else
collection
.
comment
=
comment
;
}
}
else
{
if
(
afterKey
&&
item
.
value
)
item
=
item
.
value
;
if
(
comment
===
undefined
)
{
if
(
afterKey
||
!
item
.
commentBefore
)
item
.
spaceBefore
=
true
;
}
else
{
if
(
item
.
commentBefore
)
item
.
commentBefore
+=
'\n'
+
comment
;
else
item
.
commentBefore
=
comment
;
}
}
}
}
// on error, will return { str: string, errors: Error[] }
function
resolveString
(
doc
,
node
)
{
const
res
=
node
.
strValue
;
if
(
!
res
)
return
''
;
if
(
typeof
res
===
'string'
)
return
res
;
res
.
errors
.
forEach
(
error
=>
{
if
(
!
error
.
source
)
error
.
source
=
node
;
doc
.
errors
.
push
(
error
);
});
return
res
.
str
;
}
function
resolveTagHandle
(
doc
,
node
)
{
const
{
handle
,
suffix
}
=
node
.
tag
;
let
prefix
=
doc
.
tagPrefixes
.
find
(
p
=>
p
.
handle
===
handle
);
if
(
!
prefix
)
{
const
dtp
=
doc
.
getDefaults
().
tagPrefixes
;
if
(
dtp
)
prefix
=
dtp
.
find
(
p
=>
p
.
handle
===
handle
);
if
(
!
prefix
)
throw
new
PlainValue
.
YAMLSemanticError
(
node
,
`
The
$
{
handle
}
tag
handle
is
non
-
default
and
was
not
declared
.
`
);
}
if
(
!
suffix
)
throw
new
PlainValue
.
YAMLSemanticError
(
node
,
`
The
$
{
handle
}
tag
has
no
suffix
.
`
);
if
(
handle
===
'!'
&&
(
doc
.
version
||
doc
.
options
.
version
)
===
'1.0'
)
{
if
(
suffix
[
0
]
===
'^'
)
{
doc
.
warnings
.
push
(
new
PlainValue
.
YAMLWarning
(
node
,
'YAML 1.0 ^ tag expansion is not supported'
));
return
suffix
;
}
if
(
/[:/]/
.
test
(
suffix
))
{
// word/foo -> tag:word.yaml.org,2002:foo
const
vocab
=
suffix
.
match
(
/^([a-z0-9-]+)\/(.*)/i
);
return
vocab
?
`
tag
:
$
{
vocab
[
1
]}.
yaml
.
org
,
2002
:
$
{
vocab
[
2
]}
`
:
`
tag
:
$
{
suffix
}
`
;
}
}
return
prefix
.
prefix
+
decodeURIComponent
(
suffix
);
}
function
resolveTagName
(
doc
,
node
)
{
const
{
tag
,
type
}
=
node
;
let
nonSpecific
=
false
;
if
(
tag
)
{
const
{
handle
,
suffix
,
verbatim
}
=
tag
;
if
(
verbatim
)
{
if
(
verbatim
!==
'!'
&&
verbatim
!==
'!!'
)
return
verbatim
;
const
msg
=
`
Verbatim
tags
aren
't resolved, so ${verbatim} is invalid.`;
doc.errors.push(new PlainValue.YAMLSemanticError(node, msg));
} else if (handle === '
!
' && !suffix) {
nonSpecific = true;
} else {
try {
return resolveTagHandle(doc, node);
} catch (error) {
doc.errors.push(error);
}
}
}
switch (type) {
case PlainValue.Type.BLOCK_FOLDED:
case PlainValue.Type.BLOCK_LITERAL:
case PlainValue.Type.QUOTE_DOUBLE:
case PlainValue.Type.QUOTE_SINGLE:
return PlainValue.defaultTags.STR;
case PlainValue.Type.FLOW_MAP:
case PlainValue.Type.MAP:
return PlainValue.defaultTags.MAP;
case PlainValue.Type.FLOW_SEQ:
case PlainValue.Type.SEQ:
return PlainValue.defaultTags.SEQ;
case PlainValue.Type.PLAIN:
return nonSpecific ? PlainValue.defaultTags.STR : null;
default:
return null;
}
}
function resolveByTagName(doc, node, tagName) {
const {
tags
} = doc.schema;
const matchWithTest = [];
for (const tag of tags) {
if (tag.tag === tagName) {
if (tag.test) matchWithTest.push(tag);else {
const res = tag.resolve(doc, node);
return res instanceof Collection ? res : new Scalar(res);
}
}
}
const str = resolveString(doc, node);
if (typeof str === '
string
' && matchWithTest.length > 0) return resolveScalar(str, matchWithTest, tags.scalarFallback);
return null;
}
function getFallbackTagName({
type
}) {
switch (type) {
case PlainValue.Type.FLOW_MAP:
case PlainValue.Type.MAP:
return PlainValue.defaultTags.MAP;
case PlainValue.Type.FLOW_SEQ:
case PlainValue.Type.SEQ:
return PlainValue.defaultTags.SEQ;
default:
return PlainValue.defaultTags.STR;
}
}
function resolveTag(doc, node, tagName) {
try {
const res = resolveByTagName(doc, node, tagName);
if (res) {
if (tagName && node.tag) res.tag = tagName;
return res;
}
} catch (error) {
/* istanbul ignore if */
if (!error.source) error.source = node;
doc.errors.push(error);
return null;
}
try {
const fallback = getFallbackTagName(node);
if (!fallback) throw new Error(`The tag ${tagName} is unavailable`);
const msg = `The tag ${tagName} is unavailable, falling back to ${fallback}`;
doc.warnings.push(new PlainValue.YAMLWarning(node, msg));
const res = resolveByTagName(doc, node, fallback);
res.tag = tagName;
return res;
} catch (error) {
const refError = new PlainValue.YAMLReferenceError(node, error.message);
refError.stack = error.stack;
doc.errors.push(refError);
return null;
}
}
const isCollectionItem = node => {
if (!node) return false;
const {
type
} = node;
return type === PlainValue.Type.MAP_KEY || type === PlainValue.Type.MAP_VALUE || type === PlainValue.Type.SEQ_ITEM;
};
function resolveNodeProps(errors, node) {
const comments = {
before: [],
after: []
};
let hasAnchor = false;
let hasTag = false;
const props = isCollectionItem(node.context.parent) ? node.context.parent.props.concat(node.props) : node.props;
for (const {
start,
end
} of props) {
switch (node.context.src[start]) {
case PlainValue.Char.COMMENT:
{
if (!node.commentHasRequiredWhitespace(start)) {
const msg = '
Comments
must
be
separated
from
other
tokens
by
white
space
characters
';
errors.push(new PlainValue.YAMLSemanticError(node, msg));
}
const {
header,
valueRange
} = node;
const cc = valueRange && (start > valueRange.start || header && start > header.start) ? comments.after : comments.before;
cc.push(node.context.src.slice(start + 1, end));
break;
}
// Actual anchor & tag resolution is handled by schema, here we just complain
case PlainValue.Char.ANCHOR:
if (hasAnchor) {
const msg = '
A
node
can
have
at
most
one
anchor
';
errors.push(new PlainValue.YAMLSemanticError(node, msg));
}
hasAnchor = true;
break;
case PlainValue.Char.TAG:
if (hasTag) {
const msg = '
A
node
can
have
at
most
one
tag
';
errors.push(new PlainValue.YAMLSemanticError(node, msg));
}
hasTag = true;
break;
}
}
return {
comments,
hasAnchor,
hasTag
};
}
function resolveNodeValue(doc, node) {
const {
anchors,
errors,
schema
} = doc;
if (node.type === PlainValue.Type.ALIAS) {
const name = node.rawValue;
const src = anchors.getNode(name);
if (!src) {
const msg = `Aliased anchor not found: ${name}`;
errors.push(new PlainValue.YAMLReferenceError(node, msg));
return null;
} // Lazy resolution for circular references
const res = new Alias(src);
anchors._cstAliases.push(res);
return res;
}
const tagName = resolveTagName(doc, node);
if (tagName) return resolveTag(doc, node, tagName);
if (node.type !== PlainValue.Type.PLAIN) {
const msg = `Failed to resolve ${node.type} node here`;
errors.push(new PlainValue.YAMLSyntaxError(node, msg));
return null;
}
try {
const str = resolveString(doc, node);
return resolveScalar(str, schema.tags, schema.tags.scalarFallback);
} catch (error) {
if (!error.source) error.source = node;
errors.push(error);
return null;
}
} // sets node.resolved on success
function resolveNode(doc, node) {
if (!node) return null;
if (node.error) doc.errors.push(node.error);
const {
comments,
hasAnchor,
hasTag
} = resolveNodeProps(doc.errors, node);
if (hasAnchor) {
const {
anchors
} = doc;
const name = node.anchor;
const prev = anchors.getNode(name); // At this point, aliases for any preceding node with the same anchor
// name have already been resolved, so it may safely be renamed.
if (prev) anchors.map[anchors.newName(name)] = prev; // During parsing, we need to store the CST node in anchors.map as
// anchors need to be available during resolution to allow for
// circular references.
anchors.map[name] = node;
}
if (node.type === PlainValue.Type.ALIAS && (hasAnchor || hasTag)) {
const msg = '
An
alias
node
must
not
specify
any
properties
';
doc.errors.push(new PlainValue.YAMLSemanticError(node, msg));
}
const res = resolveNodeValue(doc, node);
if (res) {
res.range = [node.range.start, node.range.end];
if (doc.options.keepCstNodes) res.cstNode = node;
if (doc.options.keepNodeTypes) res.type = node.type;
const cb = comments.before.join('
\
n
');
if (cb) {
res.commentBefore = res.commentBefore ? `${res.commentBefore}\n${cb}` : cb;
}
const ca = comments.after.join('
\
n
');
if (ca) res.comment = res.comment ? `${res.comment}\n${ca}` : ca;
}
return node.resolved = res;
}
function resolveMap(doc, cst) {
if (cst.type !== PlainValue.Type.MAP && cst.type !== PlainValue.Type.FLOW_MAP) {
const msg = `A ${cst.type} node cannot be resolved as a mapping`;
doc.errors.push(new PlainValue.YAMLSyntaxError(cst, msg));
return null;
}
const {
comments,
items
} = cst.type === PlainValue.Type.FLOW_MAP ? resolveFlowMapItems(doc, cst) : resolveBlockMapItems(doc, cst);
const map = new YAMLMap();
map.items = items;
resolveComments(map, comments);
let hasCollectionKey = false;
for (let i = 0; i < items.length; ++i) {
const {
key: iKey
} = items[i];
if (iKey instanceof Collection) hasCollectionKey = true;
if (doc.schema.merge && iKey && iKey.value === MERGE_KEY) {
items[i] = new Merge(items[i]);
const sources = items[i].value.items;
let error = null;
sources.some(node => {
if (node instanceof Alias) {
// During parsing, alias sources are CST nodes; to account for
// circular references their resolved values can'
t
be
used
here
.
const
{
type
}
=
node
.
source
;
if
(
type
===
PlainValue
.
Type
.
MAP
||
type
===
PlainValue
.
Type
.
FLOW_MAP
)
return
false
;
return
error
=
'Merge nodes aliases can only point to maps'
;
}
return
error
=
'Merge nodes can only have Alias nodes as values'
;
});
if
(
error
)
doc
.
errors
.
push
(
new
PlainValue
.
YAMLSemanticError
(
cst
,
error
));
}
else
{
for
(
let
j
=
i
+
1
;
j
<
items
.
length
;
++
j
)
{
const
{
key
:
jKey
}
=
items
[
j
];
if
(
iKey
===
jKey
||
iKey
&&
jKey
&&
Object
.
prototype
.
hasOwnProperty
.
call
(
iKey
,
'value'
)
&&
iKey
.
value
===
jKey
.
value
)
{
const
msg
=
`
Map
keys
must
be
unique
;
"${iKey}"
is
repeated
`
;
doc
.
errors
.
push
(
new
PlainValue
.
YAMLSemanticError
(
cst
,
msg
));
break
;
}
}
}
}
if
(
hasCollectionKey
&&
!
doc
.
options
.
mapAsMap
)
{
const
warn
=
'Keys with collection values will be stringified as YAML due to JS Object restrictions. Use mapAsMap: true to avoid this.'
;
doc
.
warnings
.
push
(
new
PlainValue
.
YAMLWarning
(
cst
,
warn
));
}
cst
.
resolved
=
map
;
return
map
;
}
const
valueHasPairComment
=
({
context
:
{
lineStart
,
node
,
src
},
props
})
=>
{
if
(
props
.
length
===
0
)
return
false
;
const
{
start
}
=
props
[
0
];
if
(
node
&&
start
>
node
.
valueRange
.
start
)
return
false
;
if
(
src
[
start
]
!==
PlainValue
.
Char
.
COMMENT
)
return
false
;
for
(
let
i
=
lineStart
;
i
<
start
;
++
i
)
if
(
src
[
i
]
===
'\n'
)
return
false
;
return
true
;
};
function
resolvePairComment
(
item
,
pair
)
{
if
(
!
valueHasPairComment
(
item
))
return
;
const
comment
=
item
.
getPropValue
(
0
,
PlainValue
.
Char
.
COMMENT
,
true
);
let
found
=
false
;
const
cb
=
pair
.
value
.
commentBefore
;
if
(
cb
&&
cb
.
startsWith
(
comment
))
{
pair
.
value
.
commentBefore
=
cb
.
substr
(
comment
.
length
+
1
);
found
=
true
;
}
else
{
const
cc
=
pair
.
value
.
comment
;
if
(
!
item
.
node
&&
cc
&&
cc
.
startsWith
(
comment
))
{
pair
.
value
.
comment
=
cc
.
substr
(
comment
.
length
+
1
);
found
=
true
;
}
}
if
(
found
)
pair
.
comment
=
comment
;
}
function
resolveBlockMapItems
(
doc
,
cst
)
{
const
comments
=
[];
const
items
=
[];
let
key
=
undefined
;
let
keyStart
=
null
;
for
(
let
i
=
0
;
i
<
cst
.
items
.
length
;
++
i
)
{
const
item
=
cst
.
items
[
i
];
switch
(
item
.
type
)
{
case
PlainValue
.
Type
.
BLANK_LINE
:
comments
.
push
({
afterKey
:
!!
key
,
before
:
items
.
length
});
break
;
case
PlainValue
.
Type
.
COMMENT
:
comments
.
push
({
afterKey
:
!!
key
,
before
:
items
.
length
,
comment
:
item
.
comment
});
break
;
case
PlainValue
.
Type
.
MAP_KEY
:
if
(
key
!==
undefined
)
items
.
push
(
new
Pair
(
key
));
if
(
item
.
error
)
doc
.
errors
.
push
(
item
.
error
);
key
=
resolveNode
(
doc
,
item
.
node
);
keyStart
=
null
;
break
;
case
PlainValue
.
Type
.
MAP_VALUE
:
{
if
(
key
===
undefined
)
key
=
null
;
if
(
item
.
error
)
doc
.
errors
.
push
(
item
.
error
);
if
(
!
item
.
context
.
atLineStart
&&
item
.
node
&&
item
.
node
.
type
===
PlainValue
.
Type
.
MAP
&&
!
item
.
node
.
context
.
atLineStart
)
{
const
msg
=
'Nested mappings are not allowed in compact mappings'
;
doc
.
errors
.
push
(
new
PlainValue
.
YAMLSemanticError
(
item
.
node
,
msg
));
}
let
valueNode
=
item
.
node
;
if
(
!
valueNode
&&
item
.
props
.
length
>
0
)
{
// Comments on an empty mapping value need to be preserved, so we
// need to construct a minimal empty node here to use instead of the
// missing `item.node`. -- eemeli/yaml#19
valueNode
=
new
PlainValue
.
PlainValue
(
PlainValue
.
Type
.
PLAIN
,
[]);
valueNode
.
context
=
{
parent
:
item
,
src
:
item
.
context
.
src
};
const
pos
=
item
.
range
.
start
+
1
;
valueNode
.
range
=
{
start
:
pos
,
end
:
pos
};
valueNode
.
valueRange
=
{
start
:
pos
,
end
:
pos
};
if
(
typeof
item
.
range
.
origStart
===
'number'
)
{
const
origPos
=
item
.
range
.
origStart
+
1
;
valueNode
.
range
.
origStart
=
valueNode
.
range
.
origEnd
=
origPos
;
valueNode
.
valueRange
.
origStart
=
valueNode
.
valueRange
.
origEnd
=
origPos
;
}
}
const
pair
=
new
Pair
(
key
,
resolveNode
(
doc
,
valueNode
));
resolvePairComment
(
item
,
pair
);
items
.
push
(
pair
);
if
(
key
&&
typeof
keyStart
===
'number'
)
{
if
(
item
.
range
.
start
>
keyStart
+
1024
)
doc
.
errors
.
push
(
getLongKeyError
(
cst
,
key
));
}
key
=
undefined
;
keyStart
=
null
;
}
break
;
default
:
if
(
key
!==
undefined
)
items
.
push
(
new
Pair
(
key
));
key
=
resolveNode
(
doc
,
item
);
keyStart
=
item
.
range
.
start
;
if
(
item
.
error
)
doc
.
errors
.
push
(
item
.
error
);
next
:
for
(
let
j
=
i
+
1
;;
++
j
)
{
const
nextItem
=
cst
.
items
[
j
];
switch
(
nextItem
&&
nextItem
.
type
)
{
case
PlainValue
.
Type
.
BLANK_LINE
:
case
PlainValue
.
Type
.
COMMENT
:
continue
next
;
case
PlainValue
.
Type
.
MAP_VALUE
:
break
next
;
default
:
{
const
msg
=
'Implicit map keys need to be followed by map values'
;
doc
.
errors
.
push
(
new
PlainValue
.
YAMLSemanticError
(
item
,
msg
));
break
next
;
}
}
}
if
(
item
.
valueRangeContainsNewline
)
{
const
msg
=
'Implicit map keys need to be on a single line'
;
doc
.
errors
.
push
(
new
PlainValue
.
YAMLSemanticError
(
item
,
msg
));
}
}
}
if
(
key
!==
undefined
)
items
.
push
(
new
Pair
(
key
));
return
{
comments
,
items
};
}
function
resolveFlowMapItems
(
doc
,
cst
)
{
const
comments
=
[];
const
items
=
[];
let
key
=
undefined
;
let
explicitKey
=
false
;
let
next
=
'{'
;
for
(
let
i
=
0
;
i
<
cst
.
items
.
length
;
++
i
)
{
const
item
=
cst
.
items
[
i
];
if
(
typeof
item
.
char
===
'string'
)
{
const
{
char
,
offset
}
=
item
;
if
(
char
===
'?'
&&
key
===
undefined
&&
!
explicitKey
)
{
explicitKey
=
true
;
next
=
':'
;
continue
;
}
if
(
char
===
':'
)
{
if
(
key
===
undefined
)
key
=
null
;
if
(
next
===
':'
)
{
next
=
','
;
continue
;
}
}
else
{
if
(
explicitKey
)
{
if
(
key
===
undefined
&&
char
!==
','
)
key
=
null
;
explicitKey
=
false
;
}
if
(
key
!==
undefined
)
{
items
.
push
(
new
Pair
(
key
));
key
=
undefined
;
if
(
char
===
','
)
{
next
=
':'
;
continue
;
}
}
}
if
(
char
===
'}'
)
{
if
(
i
===
cst
.
items
.
length
-
1
)
continue
;
}
else
if
(
char
===
next
)
{
next
=
':'
;
continue
;
}
const
msg
=
`
Flow
map
contains
an
unexpected
$
{
char
}
`
;
const
err
=
new
PlainValue
.
YAMLSyntaxError
(
cst
,
msg
);
err
.
offset
=
offset
;
doc
.
errors
.
push
(
err
);
}
else
if
(
item
.
type
===
PlainValue
.
Type
.
BLANK_LINE
)
{
comments
.
push
({
afterKey
:
!!
key
,
before
:
items
.
length
});
}
else
if
(
item
.
type
===
PlainValue
.
Type
.
COMMENT
)
{
checkFlowCommentSpace
(
doc
.
errors
,
item
);
comments
.
push
({
afterKey
:
!!
key
,
before
:
items
.
length
,
comment
:
item
.
comment
});
}
else
if
(
key
===
undefined
)
{
if
(
next
===
','
)
doc
.
errors
.
push
(
new
PlainValue
.
YAMLSemanticError
(
item
,
'Separator , missing in flow map'
));
key
=
resolveNode
(
doc
,
item
);
}
else
{
if
(
next
!==
','
)
doc
.
errors
.
push
(
new
PlainValue
.
YAMLSemanticError
(
item
,
'Indicator : missing in flow map entry'
));
items
.
push
(
new
Pair
(
key
,
resolveNode
(
doc
,
item
)));
key
=
undefined
;
explicitKey
=
false
;
}
}
checkFlowCollectionEnd
(
doc
.
errors
,
cst
);
if
(
key
!==
undefined
)
items
.
push
(
new
Pair
(
key
));
return
{
comments
,
items
};
}
function
resolveSeq
(
doc
,
cst
)
{
if
(
cst
.
type
!==
PlainValue
.
Type
.
SEQ
&&
cst
.
type
!==
PlainValue
.
Type
.
FLOW_SEQ
)
{
const
msg
=
`
A
$
{
cst
.
type
}
node
cannot
be
resolved
as
a
sequence
`
;
doc
.
errors
.
push
(
new
PlainValue
.
YAMLSyntaxError
(
cst
,
msg
));
return
null
;
}
const
{
comments
,
items
}
=
cst
.
type
===
PlainValue
.
Type
.
FLOW_SEQ
?
resolveFlowSeqItems
(
doc
,
cst
)
:
resolveBlockSeqItems
(
doc
,
cst
);
const
seq
=
new
YAMLSeq
();
seq
.
items
=
items
;
resolveComments
(
seq
,
comments
);
if
(
!
doc
.
options
.
mapAsMap
&&
items
.
some
(
it
=>
it
instanceof
Pair
&&
it
.
key
instanceof
Collection
))
{
const
warn
=
'Keys with collection values will be stringified as YAML due to JS Object restrictions. Use mapAsMap: true to avoid this.'
;
doc
.
warnings
.
push
(
new
PlainValue
.
YAMLWarning
(
cst
,
warn
));
}
cst
.
resolved
=
seq
;
return
seq
;
}
function
resolveBlockSeqItems
(
doc
,
cst
)
{
const
comments
=
[];
const
items
=
[];
for
(
let
i
=
0
;
i
<
cst
.
items
.
length
;
++
i
)
{
const
item
=
cst
.
items
[
i
];
switch
(
item
.
type
)
{
case
PlainValue
.
Type
.
BLANK_LINE
:
comments
.
push
({
before
:
items
.
length
});
break
;
case
PlainValue
.
Type
.
COMMENT
:
comments
.
push
({
comment
:
item
.
comment
,
before
:
items
.
length
});
break
;
case
PlainValue
.
Type
.
SEQ_ITEM
:
if
(
item
.
error
)
doc
.
errors
.
push
(
item
.
error
);
items
.
push
(
resolveNode
(
doc
,
item
.
node
));
if
(
item
.
hasProps
)
{
const
msg
=
'Sequence items cannot have tags or anchors before the - indicator'
;
doc
.
errors
.
push
(
new
PlainValue
.
YAMLSemanticError
(
item
,
msg
));
}
break
;
default
:
if
(
item
.
error
)
doc
.
errors
.
push
(
item
.
error
);
doc
.
errors
.
push
(
new
PlainValue
.
YAMLSyntaxError
(
item
,
`
Unexpected
$
{
item
.
type
}
node
in
sequence
`
));
}
}
return
{
comments
,
items
};
}
function
resolveFlowSeqItems
(
doc
,
cst
)
{
const
comments
=
[];
const
items
=
[];
let
explicitKey
=
false
;
let
key
=
undefined
;
let
keyStart
=
null
;
let
next
=
'['
;
let
prevItem
=
null
;
for
(
let
i
=
0
;
i
<
cst
.
items
.
length
;
++
i
)
{
const
item
=
cst
.
items
[
i
];
if
(
typeof
item
.
char
===
'string'
)
{
const
{
char
,
offset
}
=
item
;
if
(
char
!==
':'
&&
(
explicitKey
||
key
!==
undefined
))
{
if
(
explicitKey
&&
key
===
undefined
)
key
=
next
?
items
.
pop
()
:
null
;
items
.
push
(
new
Pair
(
key
));
explicitKey
=
false
;
key
=
undefined
;
keyStart
=
null
;
}
if
(
char
===
next
)
{
next
=
null
;
}
else
if
(
!
next
&&
char
===
'?'
)
{
explicitKey
=
true
;
}
else
if
(
next
!==
'['
&&
char
===
':'
&&
key
===
undefined
)
{
if
(
next
===
','
)
{
key
=
items
.
pop
();
if
(
key
instanceof
Pair
)
{
const
msg
=
'Chaining flow sequence pairs is invalid'
;
const
err
=
new
PlainValue
.
YAMLSemanticError
(
cst
,
msg
);
err
.
offset
=
offset
;
doc
.
errors
.
push
(
err
);
}
if
(
!
explicitKey
&&
typeof
keyStart
===
'number'
)
{
const
keyEnd
=
item
.
range
?
item
.
range
.
start
:
item
.
offset
;
if
(
keyEnd
>
keyStart
+
1024
)
doc
.
errors
.
push
(
getLongKeyError
(
cst
,
key
));
const
{
src
}
=
prevItem
.
context
;
for
(
let
i
=
keyStart
;
i
<
keyEnd
;
++
i
)
if
(
src
[
i
]
===
'\n'
)
{
const
msg
=
'Implicit keys of flow sequence pairs need to be on a single line'
;
doc
.
errors
.
push
(
new
PlainValue
.
YAMLSemanticError
(
prevItem
,
msg
));
break
;
}
}
}
else
{
key
=
null
;
}
keyStart
=
null
;
explicitKey
=
false
;
next
=
null
;
}
else
if
(
next
===
'['
||
char
!==
']'
||
i
<
cst
.
items
.
length
-
1
)
{
const
msg
=
`
Flow
sequence
contains
an
unexpected
$
{
char
}
`
;
const
err
=
new
PlainValue
.
YAMLSyntaxError
(
cst
,
msg
);
err
.
offset
=
offset
;
doc
.
errors
.
push
(
err
);
}
}
else
if
(
item
.
type
===
PlainValue
.
Type
.
BLANK_LINE
)
{
comments
.
push
({
before
:
items
.
length
});
}
else
if
(
item
.
type
===
PlainValue
.
Type
.
COMMENT
)
{
checkFlowCommentSpace
(
doc
.
errors
,
item
);
comments
.
push
({
comment
:
item
.
comment
,
before
:
items
.
length
});
}
else
{
if
(
next
)
{
const
msg
=
`
Expected
a
$
{
next
}
in
flow
sequence
`
;
doc
.
errors
.
push
(
new
PlainValue
.
YAMLSemanticError
(
item
,
msg
));
}
const
value
=
resolveNode
(
doc
,
item
);
if
(
key
===
undefined
)
{
items
.
push
(
value
);
prevItem
=
item
;
}
else
{
items
.
push
(
new
Pair
(
key
,
value
));
key
=
undefined
;
}
keyStart
=
item
.
range
.
start
;
next
=
','
;
}
}
checkFlowCollectionEnd
(
doc
.
errors
,
cst
);
if
(
key
!==
undefined
)
items
.
push
(
new
Pair
(
key
));
return
{
comments
,
items
};
}
exports
.
Alias
=
Alias
;
exports
.
Collection
=
Collection
;
exports
.
Merge
=
Merge
;
exports
.
Node
=
Node
;
exports
.
Pair
=
Pair
;
exports
.
Scalar
=
Scalar
;
exports
.
YAMLMap
=
YAMLMap
;
exports
.
YAMLSeq
=
YAMLSeq
;
exports
.
addComment
=
addComment
;
exports
.
binaryOptions
=
binaryOptions
;
exports
.
boolOptions
=
boolOptions
;
exports
.
findPair
=
findPair
;
exports
.
intOptions
=
intOptions
;
exports
.
isEmptyPath
=
isEmptyPath
;
exports
.
nullOptions
=
nullOptions
;
exports
.
resolveMap
=
resolveMap
;
exports
.
resolveNode
=
resolveNode
;
exports
.
resolveSeq
=
resolveSeq
;
exports
.
resolveString
=
resolveString
;
exports
.
strOptions
=
strOptions
;
exports
.
stringifyNumber
=
stringifyNumber
;
exports
.
stringifyString
=
stringifyString
;
exports
.
toJSON
=
toJSON
;
Event Timeline
Log In to Comment