11import encodeToRFC3986 from 'encode-3986'
22
3+ const isRfc3986Reserved = char => ':/?#[]@!$&\'()*+,;=' . indexOf ( char ) > - 1
4+ const isRrc3986Unreserved = ( char ) => {
5+ return ( / ^ [ a - z 0 - 9 \- . _ ~ ] + $ / i) . test ( char )
6+ }
7+
8+ function encodeDisallowedCharacters ( str , { escape} ) {
9+ if ( typeof str === 'number' ) {
10+ str = str . toString ( )
11+ }
12+ if ( typeof str !== 'string' || ! str . length ) {
13+ return str
14+ }
15+
16+ if ( ! escape ) {
17+ return str
18+ }
19+
20+ return str . split ( '' ) . map ( ( char ) => {
21+ if ( isRrc3986Unreserved ( char ) ) {
22+ return char
23+ }
24+
25+ if ( isRfc3986Reserved ( char ) && escape === 'unsafe' ) {
26+ return char
27+ }
28+
29+ // percent-encode: char -> ASCII code point num -> hex string -> upcase
30+ return `%${ char . charCodeAt ( 0 ) . toString ( 16 ) . toUpperCase ( ) } `
31+ } ) . join ( '' )
32+ }
33+
334export default function ( config ) {
435 const { value} = config
536
@@ -9,14 +40,13 @@ export default function (config) {
940 else if ( typeof value === 'object' ) {
1041 return encodeObject ( config )
1142 }
12-
1343 return encodePrimitive ( config )
1444}
1545
16- const escapeFn = str => encodeURIComponent ( str )
17-
1846function encodeArray ( { key, value, style, explode, escape} ) {
19- const valueEncoder = escape ? a => encodeToRFC3986 ( a ) : a => a
47+ const valueEncoder = str => encodeDisallowedCharacters ( str , {
48+ escape
49+ } )
2050
2151 if ( style === 'simple' ) {
2252 return value . map ( val => valueEncoder ( val ) ) . join ( ',' )
@@ -36,25 +66,26 @@ function encodeArray({key, value, style, explode, escape}) {
3666 }
3767
3868 if ( style === 'form' ) {
39- const commaValue = escape ? escapeFn ( ',' ) : ','
40- const after = explode ? `&${ key } =` : commaValue
69+ const after = explode ? `&${ key } =` : ','
4170 return value . map ( val => valueEncoder ( val ) ) . join ( after )
4271 }
4372
4473 if ( style === 'spaceDelimited' ) {
4574 const after = explode ? `${ key } =` : ''
46- return value . map ( val => valueEncoder ( val ) ) . join ( `${ escapeFn ( ' ' ) } ${ after } ` )
75+ return value . map ( val => valueEncoder ( val ) ) . join ( ` ${ after } ` )
4776 }
4877
4978 if ( style === 'pipeDelimited' ) {
5079 const after = explode ? `${ key } =` : ''
51- const separator = escape ? escapeFn ( '|' ) : '|'
52- return value . map ( val => valueEncoder ( val ) ) . join ( `${ separator } ${ after } ` )
80+ return value . map ( val => valueEncoder ( val ) ) . join ( `|${ after } ` )
5381 }
5482}
5583
5684function encodeObject ( { key, value, style, explode, escape} ) {
57- const valueEncoder = escape ? a => encodeToRFC3986 ( a ) : a => a
85+ const valueEncoder = str => encodeDisallowedCharacters ( str , {
86+ escape
87+ } )
88+
5889 const valueKeys = Object . keys ( value )
5990
6091 if ( style === 'simple' ) {
@@ -107,8 +138,10 @@ function encodeObject({key, value, style, explode, escape}) {
107138 }
108139}
109140
110- function encodePrimitive ( { key, value, style, explode, escape} ) {
111- const valueEncoder = escape ? a => encodeToRFC3986 ( a ) : a => a
141+ function encodePrimitive ( { key, value, style, escape} ) {
142+ const valueEncoder = str => encodeDisallowedCharacters ( str , {
143+ escape
144+ } )
112145
113146 if ( style === 'simple' ) {
114147 return valueEncoder ( value )
0 commit comments