@@ -16,7 +16,7 @@ const isEscaped = (jsonString, quotePosition) => {
1616 return Boolean ( backslashCount % 2 ) ;
1717} ;
1818
19- export default function stripJsonComments ( jsonString , { whitespace = true } = { } ) {
19+ export default function stripJsonComments ( jsonString , { whitespace = true , trailingCommas = false } = { } ) {
2020 if ( typeof jsonString !== 'string' ) {
2121 throw new TypeError ( `Expected argument \`jsonString\` to be a \`string\`, got \`${ typeof jsonString } \`` ) ;
2222 }
@@ -26,13 +26,16 @@ export default function stripJsonComments(jsonString, {whitespace = true} = {})
2626 let isInsideString = false ;
2727 let isInsideComment = false ;
2828 let offset = 0 ;
29+ let buffer = '' ;
2930 let result = '' ;
31+ let commaIndex = - 1 ;
3032
3133 for ( let index = 0 ; index < jsonString . length ; index ++ ) {
3234 const currentCharacter = jsonString [ index ] ;
3335 const nextCharacter = jsonString [ index + 1 ] ;
3436
3537 if ( ! isInsideComment && currentCharacter === '"' ) {
38+ // Enter or exit string
3639 const escaped = isEscaped ( jsonString , index ) ;
3740 if ( ! escaped ) {
3841 isInsideString = ! isInsideString ;
@@ -44,34 +47,61 @@ export default function stripJsonComments(jsonString, {whitespace = true} = {})
4447 }
4548
4649 if ( ! isInsideComment && currentCharacter + nextCharacter === '//' ) {
47- result += jsonString . slice ( offset , index ) ;
50+ // Enter single-line comment
51+ buffer += jsonString . slice ( offset , index ) ;
4852 offset = index ;
4953 isInsideComment = singleComment ;
5054 index ++ ;
5155 } else if ( isInsideComment === singleComment && currentCharacter + nextCharacter === '\r\n' ) {
56+ // Exit single-line comment via \r\n
5257 index ++ ;
5358 isInsideComment = false ;
54- result += strip ( jsonString , offset , index ) ;
59+ buffer += strip ( jsonString , offset , index ) ;
5560 offset = index ;
5661 continue ;
5762 } else if ( isInsideComment === singleComment && currentCharacter === '\n' ) {
63+ // Exit single-line comment via \n
5864 isInsideComment = false ;
59- result += strip ( jsonString , offset , index ) ;
65+ buffer += strip ( jsonString , offset , index ) ;
6066 offset = index ;
6167 } else if ( ! isInsideComment && currentCharacter + nextCharacter === '/*' ) {
62- result += jsonString . slice ( offset , index ) ;
68+ // Enter multiline comment
69+ buffer += jsonString . slice ( offset , index ) ;
6370 offset = index ;
6471 isInsideComment = multiComment ;
6572 index ++ ;
6673 continue ;
6774 } else if ( isInsideComment === multiComment && currentCharacter + nextCharacter === '*/' ) {
75+ // Exit multiline comment
6876 index ++ ;
6977 isInsideComment = false ;
70- result += strip ( jsonString , offset , index + 1 ) ;
78+ buffer += strip ( jsonString , offset , index + 1 ) ;
7179 offset = index + 1 ;
7280 continue ;
81+ } else if ( trailingCommas && ! isInsideComment ) {
82+ if ( commaIndex !== - 1 ) {
83+ if ( currentCharacter === '}' || currentCharacter === ']' ) {
84+ // Strip trailing comma
85+ buffer += jsonString . slice ( offset , index ) ;
86+ result += strip ( buffer , 0 , 1 ) + buffer . slice ( 1 ) ;
87+ buffer = '' ;
88+ offset = index ;
89+ commaIndex = - 1 ;
90+ } else if ( currentCharacter !== ' ' && currentCharacter !== '\t' && currentCharacter !== '\r' && currentCharacter !== '\n' ) {
91+ // Hit non-whitespace following a comma; comma is not trailing
92+ buffer += jsonString . slice ( offset , index ) ;
93+ offset = index ;
94+ commaIndex = - 1 ;
95+ }
96+ } else if ( currentCharacter === ',' ) {
97+ // Flush buffer prior to this point, and save new comma index
98+ result += buffer + jsonString . slice ( offset , index ) ;
99+ buffer = '' ;
100+ offset = index ;
101+ commaIndex = index ;
102+ }
73103 }
74104 }
75105
76- return result + ( isInsideComment ? strip ( jsonString . slice ( offset ) ) : jsonString . slice ( offset ) ) ;
106+ return result + buffer + ( isInsideComment ? strip ( jsonString . slice ( offset ) ) : jsonString . slice ( offset ) ) ;
77107}
0 commit comments