@@ -681,13 +681,13 @@ namespace ts {
681681 * Read tsconfig.json file
682682 * @param fileName The path to the config file
683683 */
684- export function readConfigFile ( fileName : string , readFile : ( path : string ) => string ) : { config ?: any ; error ? : Diagnostic } {
684+ export function readConfigFile ( fileName : string , readFile : ( path : string ) => string ) : { config ?: any ; errors : Diagnostic [ ] } {
685685 let text = "" ;
686686 try {
687687 text = readFile ( fileName ) ;
688688 }
689689 catch ( e ) {
690- return { error : createCompilerDiagnostic ( Diagnostics . Cannot_read_file_0_Colon_1 , fileName , e . message ) } ;
690+ return { errors : [ createCompilerDiagnostic ( Diagnostics . Cannot_read_file_0_Colon_1 , fileName , e . message ) ] } ;
691691 }
692692 return parseConfigFileTextToJson ( fileName , text ) ;
693693 }
@@ -697,13 +697,102 @@ namespace ts {
697697 * @param fileName The path to the config file
698698 * @param jsonText The text of the config file
699699 */
700- export function parseConfigFileTextToJson ( fileName : string , jsonText : string , stripComments = true ) : { config ?: any ; error ?: Diagnostic } {
701- try {
702- const jsonTextToParse = stripComments ? removeComments ( jsonText ) : jsonText ;
703- return { config : / \S / . test ( jsonTextToParse ) ? JSON . parse ( jsonTextToParse ) : { } } ;
700+ export function parseConfigFileTextToJson ( fileName : string , jsonText : string ) : { config : any ; errors : Diagnostic [ ] } {
701+ const { node, errors } = parseJsonText ( fileName , jsonText ) ;
702+ return {
703+ config : convertToJson ( node , errors ) ,
704+ errors
705+ } ;
706+ }
707+
708+ /**
709+ * Convert the json syntax tree into the json value
710+ * @param jsonNode
711+ * @param errors
712+ */
713+ function convertToJson ( jsonNode : JsonNode , errors : Diagnostic [ ] ) : any {
714+ if ( ! jsonNode ) {
715+ return undefined ;
704716 }
705- catch ( e ) {
706- return { error : createCompilerDiagnostic ( Diagnostics . Failed_to_parse_file_0_Colon_1 , fileName , e . message ) } ;
717+
718+ if ( jsonNode . kind === SyntaxKind . EndOfFileToken ) {
719+ return { } ;
720+ }
721+
722+ const sourceFile = < SourceFile > jsonNode . parent ;
723+ return convertObjectLiteralExpressionToJson ( jsonNode ) ;
724+
725+ function convertObjectLiteralExpressionToJson ( node : ObjectLiteralExpression ) : any {
726+ const result : any = { } ;
727+ for ( const element of node . properties ) {
728+ switch ( element . kind ) {
729+ case SyntaxKind . MethodDeclaration :
730+ case SyntaxKind . GetAccessor :
731+ case SyntaxKind . SetAccessor :
732+ case SyntaxKind . ShorthandPropertyAssignment :
733+ case SyntaxKind . SpreadAssignment :
734+ errors . push ( createDiagnosticForNodeInSourceFile ( sourceFile , element , Diagnostics . Property_assignment_expected ) ) ;
735+ break ;
736+
737+ case SyntaxKind . PropertyAssignment :
738+ if ( element . questionToken ) {
739+ errors . push ( createDiagnosticForNodeInSourceFile ( sourceFile , element . questionToken , Diagnostics . _0_can_only_be_used_in_a_ts_file , "?" ) ) ;
740+ }
741+ if ( ! isDoubleQuotedString ( element . name ) ) {
742+ errors . push ( createDiagnosticForNodeInSourceFile ( sourceFile , element . name , Diagnostics . String_literal_with_double_quotes_expected ) ) ;
743+ }
744+ const keyText = getTextOfPropertyName ( element . name ) ;
745+ const value = parseValue ( element . initializer ) ;
746+ if ( typeof keyText !== undefined && typeof value !== undefined ) {
747+ result [ keyText ] = value ;
748+ }
749+ }
750+ }
751+ return result ;
752+ }
753+
754+ function convertArrayLiteralExpressionToJson ( node : ArrayLiteralExpression ) : any [ ] {
755+ const result : any [ ] = [ ] ;
756+ for ( const element of node . elements ) {
757+ result . push ( parseValue ( element ) ) ;
758+ }
759+ return result ;
760+ }
761+
762+ function parseValue ( node : Expression ) : any {
763+ switch ( node . kind ) {
764+ case SyntaxKind . TrueKeyword :
765+ return true ;
766+
767+ case SyntaxKind . FalseKeyword :
768+ return false ;
769+
770+ case SyntaxKind . NullKeyword :
771+ return null ; // tslint:disable-line:no-null-keyword
772+
773+ case SyntaxKind . StringLiteral :
774+ if ( ! isDoubleQuotedString ( node ) ) {
775+ errors . push ( createDiagnosticForNodeInSourceFile ( sourceFile , node , Diagnostics . String_literal_with_double_quotes_expected ) ) ;
776+ }
777+ return ( < StringLiteral > node ) . text ;
778+
779+ case SyntaxKind . NumericLiteral :
780+ return Number ( ( < NumericLiteral > node ) . text ) ;
781+
782+ case SyntaxKind . ObjectLiteralExpression :
783+ return convertObjectLiteralExpressionToJson ( < ObjectLiteralExpression > node ) ;
784+
785+ case SyntaxKind . ArrayLiteralExpression :
786+ return convertArrayLiteralExpressionToJson ( < ArrayLiteralExpression > node ) ;
787+ }
788+
789+ // Not in expected format
790+ errors . push ( createDiagnosticForNodeInSourceFile ( sourceFile , node , Diagnostics . String_number_object_array_true_false_or_null_expected ) ) ;
791+ return undefined ;
792+ }
793+
794+ function isDoubleQuotedString ( node : Node ) {
795+ return node . kind === SyntaxKind . StringLiteral && getSourceTextOfNodeFromSourceFile ( sourceFile , node ) . charCodeAt ( 0 ) === CharacterCodes . doubleQuote ;
707796 }
708797 }
709798
@@ -795,31 +884,6 @@ namespace ts {
795884 }
796885 }
797886
798- /**
799- * Remove the comments from a json like text.
800- * Comments can be single line comments (starting with # or //) or multiline comments using / * * /
801- *
802- * This method replace comment content by whitespace rather than completely remove them to keep positions in json parsing error reporting accurate.
803- */
804- function removeComments ( jsonText : string ) : string {
805- let output = "" ;
806- const scanner = createScanner ( ScriptTarget . ES5 , /* skipTrivia */ false , LanguageVariant . Standard , jsonText ) ;
807- let token : SyntaxKind ;
808- while ( ( token = scanner . scan ( ) ) !== SyntaxKind . EndOfFileToken ) {
809- switch ( token ) {
810- case SyntaxKind . SingleLineCommentTrivia :
811- case SyntaxKind . MultiLineCommentTrivia :
812- // replace comments with whitespace to preserve original character positions
813- output += scanner . getTokenText ( ) . replace ( / \S / g, " " ) ;
814- break ;
815- default :
816- output += scanner . getTokenText ( ) ;
817- break ;
818- }
819- }
820- return output ;
821- }
822-
823887 /**
824888 * Parse the contents of a config file (tsconfig.json).
825889 * @param json The contents of the config file to parse
@@ -896,8 +960,8 @@ namespace ts {
896960 }
897961 }
898962 const extendedResult = readConfigFile ( extendedConfigPath , path => host . readFile ( path ) ) ;
899- if ( extendedResult . error ) {
900- errors . push ( extendedResult . error ) ;
963+ if ( extendedResult . errors . length ) {
964+ errors . push ( ... extendedResult . errors ) ;
901965 return ;
902966 }
903967 const extendedDirname = getDirectoryPath ( extendedConfigPath ) ;
0 commit comments