1+ /// <reference path="..\harness.ts" />
2+ /// <reference path="..\virtualFileSystem.ts" />
3+
4+ namespace ts {
5+ const testContents = {
6+ "/dev/tsconfig.json" : `{
7+ "extends": "./configs/base",
8+ "files": [
9+ "main.ts",
10+ "supplemental.ts"
11+ ]
12+ }` ,
13+ "/dev/tsconfig.nostrictnull.json" : `{
14+ "extends": "./tsconfig",
15+ "compilerOptions": {
16+ "strictNullChecks": false
17+ }
18+ }` ,
19+ "/dev/configs/base.json" : `{
20+ "compilerOptions": {
21+ "allowJs": true,
22+ "noImplicitAny": true,
23+ "strictNullChecks": true
24+ }
25+ }` ,
26+ "/dev/configs/tests.json" : `{
27+ "compilerOptions": {
28+ "preserveConstEnums": true,
29+ "removeComments": false,
30+ "sourceMap": true
31+ },
32+ "exclude": [
33+ "../tests/baselines",
34+ "../tests/scenarios"
35+ ],
36+ "include": [
37+ "../tests/**/*.ts"
38+ ]
39+ }` ,
40+ "/dev/circular.json" : `{
41+ "extends": "./circular2",
42+ "compilerOptions": {
43+ "module": "amd"
44+ }
45+ }` ,
46+ "/dev/circular2.json" : `{
47+ "extends": "./circular",
48+ "compilerOptions": {
49+ "module": "commonjs"
50+ }
51+ }` ,
52+ "/dev/missing.json" : `{
53+ "extends": "./missing2",
54+ "compilerOptions": {
55+ "types": []
56+ }
57+ }` ,
58+ "/dev/failure.json" : `{
59+ "extends": "./failure2.json",
60+ "compilerOptions": {
61+ "typeRoots": []
62+ }
63+ }` ,
64+ "/dev/failure2.json" : `{
65+ "excludes": ["*.js"]
66+ }` ,
67+ "/dev/configs/first.json" : `{
68+ "extends": "./base",
69+ "compilerOptions": {
70+ "module": "commonjs"
71+ },
72+ "files": ["../main.ts"]
73+ }` ,
74+ "/dev/configs/second.json" : `{
75+ "extends": "./base",
76+ "compilerOptions": {
77+ "module": "amd"
78+ },
79+ "include": ["../supplemental.*"]
80+ }` ,
81+ "/dev/extends.json" : `{ "extends": 42 }` ,
82+ "/dev/extends2.json" : `{ "extends": "configs/base" }` ,
83+ "/dev/main.ts" : "" ,
84+ "/dev/supplemental.ts" : "" ,
85+ "/dev/tests/unit/spec.ts" : "" ,
86+ "/dev/tests/utils.ts" : "" ,
87+ "/dev/tests/scenarios/first.json" : "" ,
88+ "/dev/tests/baselines/first/output.ts" : ""
89+ } ;
90+
91+ const caseInsensitiveBasePath = "c:/dev/" ;
92+ const caseInsensitiveHost = new Utils . MockParseConfigHost ( caseInsensitiveBasePath , /*useCaseSensitiveFileNames*/ false , mapObject ( testContents , ( key , content ) => [ `c:${ key } ` , content ] ) ) ;
93+
94+ const caseSensitiveBasePath = "/dev/" ;
95+ const caseSensitiveHost = new Utils . MockParseConfigHost ( caseSensitiveBasePath , /*useCaseSensitiveFileNames*/ true , testContents ) ;
96+
97+ function verifyDiagnostics ( actual : Diagnostic [ ] , expected : { code : number , category : DiagnosticCategory , messageText : string } [ ] ) {
98+ assert . isTrue ( expected . length === actual . length , `Expected error: ${ JSON . stringify ( expected ) } . Actual error: ${ JSON . stringify ( actual ) } .` ) ;
99+ for ( let i = 0 ; i < actual . length ; i ++ ) {
100+ const actualError = actual [ i ] ;
101+ const expectedError = expected [ i ] ;
102+ assert . equal ( actualError . code , expectedError . code , "Error code mismatch" ) ;
103+ assert . equal ( actualError . category , expectedError . category , "Category mismatch" ) ;
104+ assert . equal ( flattenDiagnosticMessageText ( actualError . messageText , "\n" ) , expectedError . messageText ) ;
105+ }
106+ }
107+
108+ describe ( "Configuration Extension" , ( ) => {
109+ forEach < [ string , string , Utils . MockParseConfigHost ] , void > ( [
110+ [ "under a case insensitive host" , caseInsensitiveBasePath , caseInsensitiveHost ] ,
111+ [ "under a case sensitive host" , caseSensitiveBasePath , caseSensitiveHost ]
112+ ] , ( [ testName , basePath , host ] ) => {
113+ function testSuccess ( name : string , entry : string , expected : CompilerOptions , expectedFiles : string [ ] ) {
114+ it ( name , ( ) => {
115+ const { config, error} = ts . readConfigFile ( entry , name => host . readFile ( name ) ) ;
116+ assert ( config && ! error , flattenDiagnosticMessageText ( error && error . messageText , "\n" ) ) ;
117+ const parsed = ts . parseJsonConfigFileContent ( config , host , basePath , { } , entry ) ;
118+ assert ( ! parsed . errors . length , flattenDiagnosticMessageText ( parsed . errors [ 0 ] && parsed . errors [ 0 ] . messageText , "\n" ) ) ;
119+ expected . configFilePath = entry ;
120+ assert . deepEqual ( parsed . options , expected ) ;
121+ assert . deepEqual ( parsed . fileNames , expectedFiles ) ;
122+ } ) ;
123+ }
124+
125+ function testFailure ( name : string , entry : string , expectedDiagnostics : { code : number , category : DiagnosticCategory , messageText : string } [ ] ) {
126+ it ( name , ( ) => {
127+ const { config, error} = ts . readConfigFile ( entry , name => host . readFile ( name ) ) ;
128+ assert ( config && ! error , flattenDiagnosticMessageText ( error && error . messageText , "\n" ) ) ;
129+ const parsed = ts . parseJsonConfigFileContent ( config , host , basePath , { } , entry ) ;
130+ verifyDiagnostics ( parsed . errors , expectedDiagnostics ) ;
131+ } ) ;
132+ }
133+
134+ describe ( testName , ( ) => {
135+ testSuccess ( "can resolve an extension with a base extension" , "tsconfig.json" , {
136+ allowJs : true ,
137+ noImplicitAny : true ,
138+ strictNullChecks : true ,
139+ } , [
140+ combinePaths ( basePath , "main.ts" ) ,
141+ combinePaths ( basePath , "supplemental.ts" ) ,
142+ ] ) ;
143+
144+ testSuccess ( "can resolve an extension with a base extension that overrides options" , "tsconfig.nostrictnull.json" , {
145+ allowJs : true ,
146+ noImplicitAny : true ,
147+ strictNullChecks : false ,
148+ } , [
149+ combinePaths ( basePath , "main.ts" ) ,
150+ combinePaths ( basePath , "supplemental.ts" ) ,
151+ ] ) ;
152+
153+ testFailure ( "can report errors on circular imports" , "circular.json" , [
154+ {
155+ code : 18000 ,
156+ category : DiagnosticCategory . Error ,
157+ messageText : `Circularity detected while resolving configuration: ${ [ combinePaths ( basePath , "circular.json" ) , combinePaths ( basePath , "circular2.json" ) , combinePaths ( basePath , "circular.json" ) ] . join ( " -> " ) } `
158+ }
159+ ] ) ;
160+
161+ testFailure ( "can report missing configurations" , "missing.json" , [ {
162+ code : 6096 ,
163+ category : DiagnosticCategory . Message ,
164+ messageText : `File './missing2' does not exist.`
165+ } ] ) ;
166+
167+ testFailure ( "can report errors in extended configs" , "failure.json" , [ {
168+ code : 6114 ,
169+ category : DiagnosticCategory . Error ,
170+ messageText : `Unknown option 'excludes'. Did you mean 'exclude'?`
171+ } ] ) ;
172+
173+ testFailure ( "can error when 'extends' is not a string" , "extends.json" , [ {
174+ code : 5024 ,
175+ category : DiagnosticCategory . Error ,
176+ messageText : `Compiler option 'extends' requires a value of type string.`
177+ } ] ) ;
178+
179+ testFailure ( "can error when 'extends' is neither relative nor rooted." , "extends2.json" , [ {
180+ code : 18001 ,
181+ category : DiagnosticCategory . Error ,
182+ messageText : `The path in an 'extends' options must be relative or rooted.`
183+ } ] ) ;
184+ } ) ;
185+ } ) ;
186+ } ) ;
187+ }
0 commit comments