2019-11-18 14:39:32 +00:00
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
2024-01-01 21:11:32 +00:00
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
2019-11-18 14:39:32 +00:00
2019-12-21 08:57:51 +00:00
import { parse , parseAll } from "./parse.ts" ;
2024-06-28 05:43:22 +00:00
import {
assert ,
assertEquals ,
assertInstanceOf ,
assertThrows ,
} from "@std/assert" ;
import { assertSpyCall , spy } from "@std/testing/mock" ;
2019-11-18 14:39:32 +00:00
2020-02-11 16:24:27 +00:00
Deno . test ( {
2024-01-02 23:45:25 +00:00
name : "parse() handles single document yaml string" ,
2022-08-24 01:21:57 +00:00
fn() {
2019-12-21 08:57:51 +00:00
const yaml = `
test : toto
foo :
bar : True
baz : 1
qux : ~
` ;
2019-11-18 14:39:32 +00:00
2019-12-21 08:57:51 +00:00
const expected = { test : "toto" , foo : { bar : true , baz : 1 , qux : null } } ;
2019-11-18 14:39:32 +00:00
2019-12-21 08:57:51 +00:00
assertEquals ( parse ( yaml ) , expected ) ;
2020-03-28 17:03:49 +00:00
} ,
2019-12-21 08:57:51 +00:00
} ) ;
2020-02-11 16:24:27 +00:00
Deno . test ( {
2024-01-02 23:45:25 +00:00
name : "parseAll() handles yaml string with multiple documents" ,
2022-08-24 01:21:57 +00:00
fn() {
2019-12-21 08:57:51 +00:00
const yaml = `
-- -
id : 1
name : Alice
-- -
id : 2
name : Bob
-- -
id : 3
name : Eve
` ;
const expected = [
{
id : 1 ,
2020-03-28 17:03:49 +00:00
name : "Alice" ,
2019-12-21 08:57:51 +00:00
} ,
{
id : 2 ,
2020-03-28 17:03:49 +00:00
name : "Bob" ,
2019-12-21 08:57:51 +00:00
} ,
{
id : 3 ,
2020-03-28 17:03:49 +00:00
name : "Eve" ,
} ,
2019-12-21 08:57:51 +00:00
] ;
assertEquals ( parseAll ( yaml ) , expected ) ;
2020-03-28 17:03:49 +00:00
} ,
2019-11-18 14:39:32 +00:00
} ) ;
2021-03-15 12:22:10 +00:00
Deno . test ( {
2024-01-02 23:45:25 +00:00
name : "parse() throws with `!!js/*` yaml types with default schemas" ,
2022-08-24 01:21:57 +00:00
fn() {
2021-03-15 12:22:10 +00:00
const yaml = ` undefined: !!js/undefined ~ ` ;
2024-08-27 05:03:26 +00:00
assertThrows (
( ) = > parse ( yaml ) ,
SyntaxError ,
"Cannot resolve unknown tag !" ,
) ;
2021-03-15 12:22:10 +00:00
} ,
} ) ;
Deno . test ( {
name :
2024-06-28 10:52:05 +00:00
"parse() handles `!!js/*` yaml types with extended schema while parsing" ,
2022-08-24 01:21:57 +00:00
fn() {
2021-03-15 12:22:10 +00:00
const yaml = `
regexp :
simple : ! ! js / regexp foobar
modifiers : ! ! js / regexp / foobar / mi
undefined : ! ! js / undefined ~
` ;
const expected = {
regexp : {
simple : /foobar/ ,
modifiers : /foobar/mi ,
} ,
undefined : undefined ,
} ;
2024-06-25 01:17:26 +00:00
assertEquals ( parse ( yaml , { schema : "extended" } ) , expected ) ;
2021-03-15 12:22:10 +00:00
} ,
} ) ;
Deno . test ( {
2024-01-02 23:45:25 +00:00
name : "parse() throws with `!!js/function` yaml type with extended schema" ,
2022-08-24 01:21:57 +00:00
fn() {
2021-03-15 12:22:10 +00:00
const func = function foobar() {
return "hello world!" ;
} ;
const yaml = `
function : ! ! js / function >
$ { func . toString ( ) . split ( "\n" ) . map ( ( line ) = > ` ${ line } ` ) . join ( "\n" ) }
` ;
2024-06-28 10:28:55 +00:00
assertThrows ( ( ) = > parse ( yaml , { schema : "extended" } ) ) ;
2021-03-15 12:22:10 +00:00
} ,
} ) ;
2021-07-21 07:47:10 +00:00
Deno . test ( {
2024-01-02 23:45:25 +00:00
name : "parseAll() accepts parse options" ,
2022-08-24 01:21:57 +00:00
fn() {
2021-07-21 07:47:10 +00:00
const yaml = `
-- -
regexp : ! ! js / regexp foo
-- -
regexp : ! ! js / regexp bar
` ;
const expected = [
{
regexp : /foo/ ,
} ,
{
regexp : /bar/ ,
} ,
] ;
2024-06-28 10:28:55 +00:00
assertEquals ( parseAll ( yaml , { schema : "extended" } ) , expected ) ;
2021-07-21 07:47:10 +00:00
} ,
} ) ;
2023-02-10 10:12:48 +00:00
Deno . test ( {
2024-01-02 23:45:25 +00:00
name : "parse() handles __proto__" ,
2023-02-10 10:12:48 +00:00
async fn() {
// Tests if the value is set using `Object.defineProperty(target, key, {value})`
// instead of `target[key] = value` when parsing the object.
// This makes a difference in behavior when __proto__ is set in Node.js and browsers.
// Using `Object.defineProperty` avoids prototype pollution in Node.js and browsers.
// reference: https://github.com/advisories/GHSA-9c47-m6qq-7p4h (CVE-2022-46175)
const yaml1 = `
__proto__ :
isAdmin : true
` ;
const yaml2 = `
anchor : & __proto__
__proto__ : 1111
alias_test :
aaa : * __proto__
merge_test :
bbb : 2222
<< : * __proto__
` ;
const testCode = `
Object . defineProperty ( Object . prototype , "__proto__" , {
set ( ) {
throw new Error ( "Don't try to set the value directly to the key __proto__." )
}
} ) ;
import { parse } from "${import.meta.resolve(" . / parse . ts ")}" ;
parse ( \ ` ${ yaml1 } \` );
parse ( \ ` ${ yaml2 } \` );
` ;
const command = new Deno . Command ( Deno . execPath ( ) , {
stdout : "inherit" ,
stderr : "inherit" ,
2024-01-12 04:38:41 +00:00
args : [ "eval" , "--no-lock" , testCode ] ,
2023-02-10 10:12:48 +00:00
} ) ;
const { success } = await command . output ( ) ;
assert ( success ) ;
} ,
} ) ;
2023-06-15 10:35:50 +00:00
Deno . test ( {
2024-01-02 23:45:25 +00:00
name : "parse() returns `null` when yaml is empty or only comments" ,
2023-06-15 10:35:50 +00:00
fn() {
const expected = null ;
const yaml1 = ` ` ;
assertEquals ( parse ( yaml1 ) , expected ) ;
const yaml2 = ` \ n \ n ` ;
assertEquals ( parse ( yaml2 ) , expected ) ;
const yaml3 = ` # just a bunch of comments \ n # in this file ` ;
assertEquals ( parse ( yaml3 ) , expected ) ;
} ,
} ) ;
2023-08-31 04:48:11 +00:00
Deno . test ( {
2024-01-02 23:45:25 +00:00
name : "parse() handles binary type" ,
2023-08-31 04:48:11 +00:00
fn() {
const yaml = ` message: !!binary "SGVsbG8=" ` ;
assertEquals ( parse ( yaml ) , {
message : new Uint8Array ( [ 72 , 101 , 108 , 108 , 111 ] ) ,
} ) ;
2024-07-02 09:26:23 +00:00
// ignore CR LF in base64 string
assertEquals (
parse ( ` message: !!binary |
YWJjZGVmZ \ r
2 hpamtsbW \ r
5 vcHFyc3R \ r
1 dnd4eXo =
` ),
{
// deno-fmt-ignore
message : new Uint8Array ( [ 97 , 98 , 99 , 100 , 101 , 102 , 103 , 104 , 105 , 106 , 107 , 108 , 109 , 110 , 111 , 112 , 113 , 114 , 115 , 116 , 117 , 118 , 119 , 120 , 121 , 122 ] ) ,
} ,
) ;
// check tailbits handling
// 2 padding characters
assertEquals (
parse ( ` message: !!binary "AQ==" ` ) ,
{
message : new Uint8Array ( [ 1 ] ) ,
} ,
) ;
// 1 padding character
assertEquals (
parse ( ` message: !!binary "AQI=" ` ) ,
{
message : new Uint8Array ( [ 1 , 2 ] ) ,
} ,
) ;
// no padding character
assertEquals (
parse ( ` message: !!binary "AQID" ` ) ,
{
message : new Uint8Array ( [ 1 , 2 , 3 ] ) ,
} ,
) ;
// invalid base64 string
assertThrows (
( ) = > parse ( "message: !!binary <>" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:binary> explicit tag at line 1, column 21:\n message: !!binary <>\n ^" ,
2024-07-02 09:26:23 +00:00
) ;
// empty base64 string is error
assertThrows (
( ) = > parse ( "message: !!binary" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:binary> explicit tag at line 2, column 1:\n \n ^" ,
2024-07-02 09:26:23 +00:00
) ;
2023-08-31 04:48:11 +00:00
} ,
} ) ;
2024-05-24 10:01:37 +00:00
2024-07-02 10:49:10 +00:00
Deno . test ( {
name : "parse() handles boolean types" ,
fn() {
assertEquals (
parse ( `
- true
- True
- TRUE
- false
- False
- FALSE
` ),
[ true , true , true , false , false , false ] ,
) ;
// if the boolean names are in strange casing, they become strings
assertEquals (
parse ( `
- TruE
- tRue
- FaLsE
- fAlSe
` ),
[ "TruE" , "tRue" , "FaLsE" , "fAlSe" ] ,
) ;
// Yes, No, On, Off are not booleans in YAML 1.2
assertEquals (
parse ( `
- yes
- Yes
- YES
- no
- No
- NO
- on
- On
- ON
- off
- Off
- OFF
` ),
[
"yes" ,
"Yes" ,
"YES" ,
"no" ,
"No" ,
"NO" ,
"on" ,
"On" ,
"ON" ,
"off" ,
"Off" ,
"OFF" ,
] ,
) ;
} ,
} ) ;
2024-05-24 10:01:37 +00:00
Deno . test ( {
name : "parse() handles float types" ,
fn() {
const yaml = `
- 3.14
- - 3.14
- . inf
- - . inf
- . nan
- 12 e03
- 1 :15
- 1 :15 : 20
- - 1 :15 : 20
- ! ! float 12000
` ;
assertEquals ( parse ( yaml ) , [
3.14 ,
- 3.14 ,
Infinity ,
- Infinity ,
NaN ,
12000 ,
2024-07-25 06:46:44 +00:00
"1:15" ,
"1:15:20" ,
"-1:15:20" ,
2024-05-24 10:01:37 +00:00
12000 ,
] ) ;
} ,
} ) ;
2024-05-24 12:30:38 +00:00
2024-07-02 12:06:38 +00:00
Deno . test ( {
name : "parse() handles integer types" ,
fn() {
assertEquals (
parse ( `
- 0
2024-08-30 00:02:13 +00:00
- + 0
- - 0
2024-07-02 12:06:38 +00:00
- 42
2024-08-30 00:02:13 +00:00
- + 42
2024-07-02 12:06:38 +00:00
- - 42
- 1 _000
` ),
2024-08-30 00:02:13 +00:00
[ 0 , 0 , 0 , 42 , 42 , - 42 , 1000 ] ,
2024-07-02 12:06:38 +00:00
) ;
// binary, octal, and hexadecimal
assertEquals (
parse ( `
- 0 b1010
- 0 b1010_1010
- 012
- 012 _34
- 0x1A
- 0x1A _2B
` ),
[ 10 , 170 , 10 , 668 , 26 , 6699 ] ,
) ;
// empty string is invalid integer
assertThrows (
( ) = > parse ( '!!int ""' ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
'Cannot resolve a node with !<tag:yaml.org,2002:int> explicit tag at line 1, column 9:\n !!int ""\n ^' ,
2024-07-02 12:06:38 +00:00
) ;
// number can't start with _
assertThrows (
( ) = > parse ( "!!int _42" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:int> explicit tag at line 2, column 1:\n \n ^" ,
2024-07-02 12:06:38 +00:00
) ;
// number can't end with _
assertThrows (
( ) = > parse ( "!!int 42_" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:int> explicit tag at line 2, column 1:\n \n ^" ,
2024-07-02 12:06:38 +00:00
) ;
// invalid binary number
assertThrows (
( ) = > parse ( "!!int 0b102" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:int> explicit tag at line 2, column 1:\n \n ^" ,
2024-07-02 12:06:38 +00:00
) ;
// invalid octal number
assertThrows (
( ) = > parse ( "!!int 09" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:int> explicit tag at line 2, column 1:\n \n ^" ,
2024-07-02 12:06:38 +00:00
) ;
// invalid hexadecimal number
assertThrows (
( ) = > parse ( "!!int 0x1G" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:int> explicit tag at line 2, column 1:\n \n ^" ,
2024-07-02 12:06:38 +00:00
) ;
} ,
} ) ;
2024-07-02 01:48:22 +00:00
Deno . test ( {
name : "parse() handles timestamp types" ,
fn() {
assertEquals (
parse ( `
- 2001 - 12 - 15T02 :59 : 43.1 Z
- 2001 - 12 - 14t21 :59 : 43.10 - 05 :00
2024-09-02 02:22:12 +00:00
- 2001 - 12 - 14t21 :59 : 43.10 + 05 :00
2024-07-02 01:48:22 +00:00
- 2001 - 12 - 14 21 :59 : 43.10 - 5
2024-09-02 02:22:12 +00:00
- 2001 - 12 - 14 21 :59 : 43.10 + 5
2024-07-02 01:48:22 +00:00
- 2002 - 12 - 14 ` ),
[
new Date ( Date . UTC ( 2001 , 11 , 15 , 2 , 59 , 43 , 100 ) ) ,
new Date ( "2001-12-14T21:59:43.100-05:00" ) ,
2024-09-02 02:22:12 +00:00
new Date ( "2001-12-14T21:59:43.100+05:00" ) ,
2024-07-02 01:48:22 +00:00
new Date ( "2001-12-14T21:59:43.100-05:00" ) ,
2024-09-02 02:22:12 +00:00
new Date ( "2001-12-14T21:59:43.100+05:00" ) ,
2024-07-02 01:48:22 +00:00
new Date ( "2002-12-14" ) ,
] ,
) ;
assertThrows (
( ) = > parse ( "- !!timestamp" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:timestamp> explicit tag at line 2, column 1:\n \n ^" ,
2024-07-02 01:48:22 +00:00
) ;
assertThrows (
( ) = > parse ( "- !!timestamp 1" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:timestamp> explicit tag at line 1, column 16:\n - !!timestamp 1\n ^" ,
2024-07-02 01:48:22 +00:00
) ;
} ,
} ) ;
2024-05-24 12:30:38 +00:00
Deno . test ( {
name : "parse() handles omap type" ,
fn() {
const yaml = ` --- !!omap
- Mark McGwire : 65
- Sammy Sosa : 63
- Ken Griffey : 58
` ;
assertEquals ( parse ( yaml ) , [
{ "Mark McGwire" : 65 } ,
{ "Sammy Sosa" : 63 } ,
{ "Ken Griffey" : 58 } ,
] ) ;
// Invalid omap
// map entry is not an object
assertThrows (
( ) = > parse ( "--- !!omap\n- 1" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:omap> explicit tag" ,
2024-05-24 12:30:38 +00:00
) ;
// map entry is empty object
assertThrows (
( ) = > parse ( "--- !!omap\n- {}" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:omap> explicit tag" ,
2024-05-24 12:30:38 +00:00
) ;
// map entry is an object with multiple keys
assertThrows (
( ) = > parse ( "--- !!omap\n- foo: 1\n bar: 2" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:omap> explicit tag" ,
2024-05-24 12:30:38 +00:00
) ;
// 2 map entries have the same key
assertThrows (
( ) = > parse ( "--- !!omap\n- foo: 1\n- foo: 2" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:omap> explicit tag" ,
2024-05-24 12:30:38 +00:00
) ;
} ,
} ) ;
2024-06-28 05:43:22 +00:00
2024-06-29 07:47:35 +00:00
Deno . test ( "parse() handles !!pairs type" , ( ) = > {
assertEquals (
parse ( ` !!pairs
- Monday : 3
- Tuesday : 4 ` ),
[
[ "Monday" , 3 ] ,
[ "Tuesday" , 4 ] ,
] ,
) ;
// empty pairs
assertEquals (
parse ( ` !!pairs ` ) ,
[ ] ,
) ;
// invalid pairs
assertThrows (
// pair is not an object
( ) = > parse ( ` !!pairs \ n- 1 ` ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:pairs> explicit tag" ,
2024-06-29 07:47:35 +00:00
) ;
assertThrows (
// pair is object with multiple keys
( ) = > parse ( ` !!pairs \ n- { Monday: 3, Tuesday: 4 } ` ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot resolve a node with !<tag:yaml.org,2002:pairs> explicit tag" ,
2024-06-29 07:47:35 +00:00
) ;
} ) ;
2024-06-29 07:38:30 +00:00
Deno . test ( "parse() handles anchors and aliases" , ( ) = > {
assertEquals (
parse ( ` - &anchor Foo
- * anchor ` ),
[ "Foo" , "Foo" ] ,
) ;
assertEquals (
parse ( ` - &anchor 1
- * anchor ` ),
[ 1 , 1 ] ,
) ;
assertEquals (
parse ( ` - &anchor { Monday: 3, Tuesday: 4 }
- * anchor ` ),
[ { Monday : 3 , Tuesday : 4 } , { Monday : 3 , Tuesday : 4 } ] ,
) ;
assertEquals (
parse ( ` - &anchor
Monday : 3
Tuesday : 4
- << : * anchor
Wednesday : 5 ` ),
[
{ Monday : 3 , Tuesday : 4 } ,
{ Monday : 3 , Tuesday : 4 , Wednesday : 5 } ,
] ,
) ;
assertEquals (
parse ( ` - &anchor !!binary "SGVsbG8="
- * anchor ` ),
[
new Uint8Array ( [ 72 , 101 , 108 , 108 , 111 ] ) ,
new Uint8Array ( [ 72 , 101 , 108 , 108 , 111 ] ) ,
] ,
) ;
assertThrows (
( ) = >
parse ( ` - &anchor Foo
- * anchor2 ` ),
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-06-29 07:38:30 +00:00
'unidentified alias "anchor2" at line 2, column 11:\n - *anchor2\n ^' ,
) ;
assertThrows (
( ) = >
parse ( ` - &anchor Foo
- * ` ),
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot read alias: alias name must contain at least one character at line 2, column 4:\n - *\n ^" ,
2024-06-29 07:38:30 +00:00
) ;
} ) ;
2024-06-28 05:43:22 +00:00
Deno . test ( "parse() handles escaped strings in double quotes" , ( ) = > {
assertEquals ( parse ( '"\\"bar\\""' ) , '"bar"' ) ;
assertEquals ( parse ( '"\\x30\\x31"' ) , "01" ) ;
assertEquals ( parse ( '"\\\na"' ) , "a" ) ;
assertEquals ( parse ( '"\\x4a"' ) , "J" ) ;
assertEquals ( parse ( '"\\u0041"' ) , "A" ) ;
assertEquals ( parse ( '"\\U00000041"' ) , "A" ) ;
assertEquals ( parse ( '"\\U0001F431"' ) , "🐱" ) ;
assertThrows (
// invalid hex character
( ) = > parse ( '"\\xyz"' ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-06-28 05:43:22 +00:00
'expected hexadecimal character at line 1, column 4:\n "\\xyz"\n ^' ,
) ;
assertThrows (
// invalid escape sequence
( ) = > parse ( '"\\X30"' ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-06-28 05:43:22 +00:00
'unknown escape sequence at line 1, column 3:\n "\\X30"\n ^' ,
) ;
} ) ;
2024-07-09 03:13:35 +00:00
Deno . test ( "parse() handles single quoted scalar" , ( ) = > {
assertEquals ( parse ( "'bar'" ) , "bar" ) ;
// escaped single quote
assertEquals ( parse ( "'''bar'''" ) , "'bar'" ) ;
// line break in single quoted scalar
assertEquals ( parse ( "'foo\nbar'" ) , "foo bar" ) ;
assertThrows (
// document end in single quoted scalar
( ) = > parse ( "'bar\n" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Unexpected end of the stream within a single quoted scalar at line 2, column 1:\n \n ^" ,
2024-07-09 03:13:35 +00:00
) ;
assertThrows (
// document separator appears in single quoted scalar
( ) = > parse ( "'bar\n..." ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Unexpected end of the document within a single quoted scalar at line 2, column 1:\n ...\n ^" ,
2024-07-09 03:13:35 +00:00
) ;
} ) ;
2024-06-28 05:43:22 +00:00
Deno . test ( "parse() handles %YAML directive" , ( ) = > {
assertEquals (
parse ( ` %YAML 1.2
-- -
2024-06-28 11:46:09 +00:00
hello : world ` ),
{ hello : "world" } ,
) ;
// you can write comments after the directive
assertEquals (
parse ( ` %YAML 1.2 # comment
-- -
2024-06-28 05:43:22 +00:00
hello : world ` ),
{ hello : "world" } ,
) ;
assertThrows (
( ) = >
parse ( ` %YAML 1.2
% YAML 1.2
-- -
hello : world ` ),
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-06-28 05:43:22 +00:00
"duplication of %YAML directive at line 3, column 1:\n ---\n ^" ,
) ;
assertThrows (
( ) = >
parse ( ` %YAML 1.2 1.1
-- -
hello : world ` ),
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot handle YAML directive: YAML directive accepts exactly one argument at line 2, column 1:\n ---\n ^" ,
2024-06-28 05:43:22 +00:00
) ;
assertThrows (
( ) = >
parse ( ` %YAML 1.2.3
-- -
hello : world ` ),
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot handle YAML directive: ill-formed argument at line 2, column 1:\n ---\n ^" ,
2024-06-28 05:43:22 +00:00
) ;
assertThrows (
( ) = >
parse ( ` %YAML 2.0
-- -
hello : world ` ),
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot handle YAML directive: unacceptable YAML version at line 2, column 1:\n ---\n ^" ,
2024-06-28 05:43:22 +00:00
) ;
assertEquals (
// The future 1.x version is acceptable
parse ( ` %YAML 1.3
-- -
hello : world ` ),
{ hello : "world" } ,
) ;
{
const warningSpy = spy ( ) ;
assertEquals (
parse (
` %YAML 1.3
-- -
hello : world ` ,
{ onWarning : warningSpy } ,
) ,
{ hello : "world" } ,
) ;
assertSpyCall ( warningSpy , 0 ) ;
const warning = warningSpy . calls [ 0 ] ? . args [ 0 ] ;
assertEquals (
warning . message ,
2024-08-27 05:03:26 +00:00
"Cannot handle YAML directive: unsupported YAML version at line 2, column 1:\n ---\n ^" ,
2024-06-28 05:43:22 +00:00
) ;
2024-07-16 10:12:33 +00:00
assertInstanceOf ( warning , SyntaxError ) ;
2024-06-28 05:43:22 +00:00
}
} ) ;
Deno . test ( "parse() handles %TAG directive" , ( ) = > {
assertEquals (
parse ( ` %TAG ! tag:example.com,2000:
-- -
hello : world ` ),
{ hello : "world" } ,
) ;
assertThrows (
( ) = >
parse ( ` %TAG !
-- -
hello : world ` ),
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot handle tag directive: directive accepts exactly two arguments, received 1 at line 2, column 1:\n ---\n ^" ,
2024-06-28 05:43:22 +00:00
) ;
assertThrows (
( ) = >
parse ( ` %TAG abc tag:example.com,2000:
-- -
hello : world ` ),
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
'Cannot handle tag directive: ill-formed handle (first argument) in "abc" at line 2, column 1:\n ---\n ^' ,
2024-06-28 05:43:22 +00:00
) ;
assertThrows (
( ) = >
parse ( ` %TAG ! tag:example.com,2000:
% TAG ! tag :example.com , 2000 :
-- -
hello : world ` ),
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
'Cannot handle tag directive: previously declared suffix for "!" tag handle at line 3, column 1:\n ---\n ^' ,
2024-06-28 05:43:22 +00:00
) ;
assertThrows (
( ) = >
parse ( ` %TAG ! ,:
-- -
hello : world ` ),
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-06-28 05:43:22 +00:00
"ill-formed tag prefix (second argument) of the TAG directive at line 2, column 1:\n ---\n ^" ,
) ;
} ) ;
Deno . test ( "parse() throws with invalid strings" , ( ) = > {
2024-08-27 05:03:26 +00:00
assertThrows ( ( ) = > parse ( ` " ` ) , SyntaxError , "Unexpected end of the stream" ) ;
2024-06-28 05:43:22 +00:00
assertThrows (
( ) = > parse ( ` " \ x08" ` ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
'Expected valid JSON character: received "8" at line 1, column 3:\n "\b"\n ^' ,
2024-06-28 05:43:22 +00:00
) ;
2024-06-28 10:52:05 +00:00
// non-printable char in block scalar
assertThrows (
( ) = > parse ( ` foo: | \ n \ x08 ` ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Stream contains non-printable characters at line 2, column 4:\n \b\n ^" ,
2024-06-28 10:52:05 +00:00
) ;
2024-06-28 05:43:22 +00:00
} ) ;
2024-06-28 06:05:37 +00:00
Deno . test ( "parse() handles merge (<<) types" , ( ) = > {
assertEquals (
parse ( ` <<: { a: 1, b: 2 }
c : 3 ` ),
{ a : 1 , b : 2 , c : 3 } ,
) ;
2024-07-20 10:02:55 +00:00
assertEquals (
parse ( ` <<: [{ a: 1 }, { b: 2 }]
c : 1 ` ),
{ a : 1 , b : 2 , c : 1 } ,
) ;
2024-06-28 06:05:37 +00:00
assertThrows (
( ) = >
// number can't be used as merge value
parse ( ` <<: 1
c : 3 ` ),
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot merge mappings: the provided source object is unacceptable at line 1, column 6:\n <<: 1\n ^" ,
2024-06-28 06:05:37 +00:00
) ;
} ) ;
2024-06-28 10:52:05 +00:00
// 8.1. Block Scalar Styles
// https://yaml.org/spec/1.2.2/#81-block-scalar-styles
Deno . test ( "parse() handles block scalars" , ( ) = > {
assertEquals (
parse ( ` foo: |
bar ` ),
{ foo : "bar\n" } ,
) ;
assertEquals (
parse ( ` foo: |
bar
bar ` ),
{ foo : "bar\n\nbar\n" } ,
) ;
assertEquals (
parse ( ` foo: |+
bar ` ),
{ foo : "bar\n" } ,
) ;
assertEquals (
parse ( ` foo: |+
bar : baz ` ),
{ foo : "" , bar : "baz" } ,
) ;
assertEquals (
parse ( ` foo: |-
bar ` ),
{ foo : "bar" } ,
) ;
assertThrows (
( ) = > parse ( ` foo: |++ \ n bar ` ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot read block: chomping mode identifier repeated at line 1, column 8:\n foo: |++\n ^" ,
2024-06-28 10:52:05 +00:00
) ;
assertEquals (
parse ( ` foo: |1
bar ` ),
{ foo : " bar\n" } ,
) ;
assertEquals (
parse ( ` foo: |
bar ` ),
{ foo : "\nbar\n" } ,
) ;
assertEquals (
parse ( ` foo: | \ n bar ` ) ,
{ foo : "bar\n" } ,
) ;
assertEquals (
parse ( ` foo: | # comment \ n bar ` ) ,
{ foo : "bar\n" } ,
) ;
assertThrows (
( ) = > parse ( ` foo: |0 \ n bar ` ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot read block: indentation width must be greater than 0 at line 1, column 7:\n foo: |0\n ^" ,
2024-06-28 10:52:05 +00:00
) ;
assertThrows (
( ) = > parse ( ` foo: |11 \ n bar ` ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot read block: indentation width identifier repeated at line 1, column 8:\n foo: |11\n ^" ,
2024-06-28 10:52:05 +00:00
) ;
// folding style (>)
assertEquals ( parse ( ` foo: >1 \ n bar ` ) , { foo : " bar\n" } ) ;
assertEquals (
parse ( ` foo: >
a
b
c
- d
- e
f
g
` ),
{ foo : "\na b\nc\n - d\n - e\n\nf g\n" } ,
) ;
} ) ;
2024-06-28 11:46:09 +00:00
Deno . test ( "parse() handles BOM at the beginning of the yaml" , ( ) = > {
const yaml = "\uFEFFhello: world" ;
assertEquals ( parse ( yaml ) , { hello : "world" } ) ;
} ) ;
Deno . test ( "parse() throws if there are more than one document in the yaml" , ( ) = > {
assertThrows (
( ) = > parse ( "hello: world\n---\nfoo: bar" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Found more than 1 document in the stream: expected a single document" ,
2024-06-28 11:46:09 +00:00
) ;
} ) ;
Deno . test ( "parse() throws when the directive name is empty" , ( ) = > {
assertThrows (
( ) = >
parse ( ` % 1.2
-- -
hello : world ` ),
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot read document: directive name length must be greater than zero at line 1, column 2:\n % 1.2\n ^" ,
2024-06-28 11:46:09 +00:00
) ;
} ) ;
Deno . test ( "parse() ignores unknown directive" , ( ) = > {
assertEquals (
parse ( ` %FOOBAR 1.2
-- -
hello : world ` ),
{ hello : "world" } ,
) ;
} ) ;
Deno . test ( "parse() handles document separator" , ( ) = > {
assertEquals (
parse ( ` ---
hello : world
. . . ` ),
{ hello : "world" } ,
) ;
} ) ;
Deno . test ( "parse() show warning with non-ascii line breaks if YAML version is < 1.2" , ( ) = > {
const warningSpy = spy ( ) ;
assertEquals (
parse (
` %YAML 1.1
-- -
hello : world \ r \ nfoo : bar \ x85 ` ,
{ onWarning : warningSpy } ,
) ,
{ hello : "world" , foo : "bar\x85" } ,
) ;
assertSpyCall ( warningSpy , 0 ) ;
const warning = warningSpy . calls [ 0 ] ? . args [ 0 ] ;
assertEquals (
warning . message ,
"non-ASCII line breaks are interpreted as content at line 5, column 1:\n \n ^" ,
) ;
} ) ;
Deno . test ( "parse() throws with directive only document" , ( ) = > {
assertThrows (
( ) = > parse ( ` %YAML 1.2 ` ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-06-28 11:46:09 +00:00
"directives end mark is expected at line 2, column 1:\n \n ^" ,
) ;
} ) ;
Deno . test ( "parse() throws with \0 in the middle of the document" , ( ) = > {
assertThrows (
( ) = > parse ( "hello: \0 world" ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-06-28 11:46:09 +00:00
"end of the stream or a document separator is expected at line 1, column 8:\n hello: \n ^" ,
) ;
} ) ;
2024-07-02 01:51:53 +00:00
Deno . test ( "parse() handles complex mapping key" , ( ) = > {
assertEquals (
parse ( ` ? - Detroit Tigers
- Chicago cubs
: - 2001 - 07 - 23
? [ New York Yankees ,
Atlanta Braves ]
: [ 2001 - 07 - 02 , 2001 - 08 - 12 ,
2001 - 08 - 14 ] ` ),
{
"Detroit Tigers,Chicago cubs" : [ new Date ( "2001-07-23" ) ] ,
"New York Yankees,Atlanta Braves" : [
new Date ( "2001-07-02" ) ,
new Date ( "2001-08-12" ) ,
new Date ( "2001-08-14" ) ,
] ,
} ,
) ;
// Nested array as key is not supported
assertThrows (
( ) = >
parse ( ` ? - [ foo ]
: bar ` ),
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-07-02 01:51:53 +00:00
"nested arrays are not supported inside keys at line 2, column 6:\n : bar\n ^" ,
) ;
assertEquals (
parse ( ` ? - { foo: bar }
: baz ` ),
{ "[object Object]" : "baz" } ,
) ;
assertEquals (
parse ( ` ? { foo: bar }
: baz ` ),
{ "[object Object]" : "baz" } ,
) ;
} ) ;
Deno . test ( "parse() handles unordered set" , ( ) = > {
assertEquals (
parse ( ` --- !!set
? Mark McGwire
? Sammy Sosa
? Ken Griffey ` ),
{ "Mark McGwire" : null , "Sammy Sosa" : null , "Ken Griffey" : null } ,
) ;
2024-08-29 23:35:49 +00:00
assertEquals ( parse ( ` --- !!set ` ) , { } ) ;
2024-07-02 01:51:53 +00:00
} ) ;
Deno . test ( "parse() throws with empty mapping key" , ( ) = > {
assertThrows (
( ) = > parse ( ` ? : 1 ` ) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot read block as explicit mapping pair is incomplete: a key node is missed or followed by a non-tabulated empty line at line 1, column 3:\n ? : 1\n ^" ,
2024-07-02 01:51:53 +00:00
) ;
} ) ;
2024-07-02 03:58:47 +00:00
Deno . test ( "parse() throws on duplicate keys" , ( ) = > {
assertThrows (
( ) = >
parse (
` name: John Doe
age : 30
name : Jane Doe ` ,
) ,
2024-07-16 10:12:33 +00:00
SyntaxError ,
2024-08-27 05:03:26 +00:00
"Cannot store mapping pair: duplicated key at line 3, column 1:\n name: Jane Doe\n ^" ,
2024-07-02 03:58:47 +00:00
) ;
} ) ;
2024-07-04 03:59:09 +00:00
2024-07-04 04:59:06 +00:00
Deno . test ( "parse() allows duplicate keys when `allowDuplicateKeys` option is set to `true`" , ( ) = > {
2024-07-04 03:59:09 +00:00
assertEquals (
parse (
` name: John Doe
age : 30
name : Jane Doe ` ,
2024-07-04 04:59:06 +00:00
{ allowDuplicateKeys : true } ,
2024-07-04 03:59:09 +00:00
) ,
{ name : "Jane Doe" , age : 30 } ,
) ;
} ) ;
2024-07-23 06:12:20 +00:00
Deno . test ( "parse() throws at reseverd characters '`' and '@'" , ( ) = > {
assertThrows (
( ) = > parse ( "`" ) ,
SyntaxError ,
"end of the stream or a document separator is expected at line 1, column 1:\n `\n ^" ,
) ;
assertThrows (
( ) = > parse ( "@" ) ,
SyntaxError ,
"end of the stream or a document separator is expected at line 1, column 1:\n @\n ^" ,
) ;
} ) ;
2024-07-24 07:26:01 +00:00
2024-07-24 07:44:06 +00:00
Deno . test ( "parse() handles sequence" , ( ) = > {
assertEquals ( parse ( "[]" ) , [ ] ) ;
assertEquals (
parse ( "[ Clark Evans, Ingy döt Net, Oren Ben-Kiki ]" ) ,
[ "Clark Evans" , "Ingy döt Net" , "Oren Ben-Kiki" ] ,
) ;
assertEquals ( parse ( "!!seq []" ) , [ ] ) ;
assertEquals (
parse ( "!!seq [ Clark Evans, Ingy döt Net, Oren Ben-Kiki ]" ) ,
[ "Clark Evans" , "Ingy döt Net" , "Oren Ben-Kiki" ] ,
) ;
assertEquals (
parse ( ` !!seq
` ),
[ ] ,
) ;
assertEquals (
parse ( ` !!seq
- Clark Evans
- Ingy döt Net
- Oren Ben - Kiki ` ),
[ "Clark Evans" , "Ingy döt Net" , "Oren Ben-Kiki" ] ,
) ;
} ) ;
2024-07-24 07:32:17 +00:00
Deno . test ( "parse() handles mapping" , ( ) = > {
assertEquals ( parse ( "{}" ) , { } ) ;
assertEquals (
parse ( "{ Clark: Evans, Ingy: döt Net, Oren: Ben-Kiki }" ) ,
{ Clark : "Evans" , Ingy : "döt Net" , Oren : "Ben-Kiki" } ,
) ;
assertEquals ( parse ( "!!map {}" ) , { } ) ;
assertEquals (
parse ( "!!map { Clark: Evans, Ingy: döt Net, Oren: Ben-Kiki }" ) ,
{ Clark : "Evans" , Ingy : "döt Net" , Oren : "Ben-Kiki" } ,
) ;
assertEquals (
parse ( ` !!map
` ),
{ } ,
) ;
assertEquals (
parse ( ` !!map
Clark : Evans
Ingy : döt Net
Oren : Ben - Kiki ` ),
{ Clark : "Evans" , Ingy : "döt Net" , Oren : "Ben-Kiki" } ,
) ;
} ) ;
2024-07-24 07:26:01 +00:00
Deno . test ( "parse() handles string" , ( ) = > {
assertEquals ( parse ( "!!str" ) , "" ) ;
assertEquals ( parse ( "!!str 2002-04-28" ) , "2002-04-28" ) ;
} ) ;
2024-08-29 22:53:08 +00:00
Deno . test ( {
name : "parse() handles regexp value with extended schema option" ,
fn() {
assertEquals (
parse ( "!<tag:yaml.org,2002:js/regexp> ^foo$\n" , { schema : "extended" } ) ,
/^foo$/ ,
) ;
assertEquals (
parse ( "!<tag:yaml.org,2002:js/regexp> /^foo$/\n" , { schema : "extended" } ) ,
/^foo$/ ,
) ;
assertEquals (
parse ( "!<tag:yaml.org,2002:js/regexp> /^foo$/g\n" , {
schema : "extended" ,
} ) ,
/^foo$/g ,
) ;
assertThrows (
( ) = >
parse ( "!<tag:yaml.org,2002:js/regexp> /^foo$/gg\n" , {
schema : "extended" ,
} ) ,
SyntaxError ,
"Cannot resolve a node" ,
) ;
assertThrows (
( ) = >
parse ( "!<tag:yaml.org,2002:js/regexp> \n" , {
schema : "extended" ,
} ) ,
SyntaxError ,
"Cannot resolve a node" ,
) ;
assertThrows (
( ) = >
parse ( "!<tag:yaml.org,2002:js/regexp> /\n" , {
schema : "extended" ,
} ) ,
SyntaxError ,
"Cannot resolve a node" ,
) ;
} ,
} ) ;