diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index c4e079c6a13..686af79a6cc 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -912,6 +912,9 @@ namespace ts { if (hasProperty(json, "files")) { if (isArray(json["files"])) { fileNames = json["files"]; + if (fileNames.length === 0) { + errors.push(createCompilerDiagnostic(Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json")); + } } else { errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "files", "Array")); @@ -954,7 +957,18 @@ namespace ts { includeSpecs = ["**/*"]; } - return matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors); + const result = matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors); + + if (result.fileNames.length === 0 && !hasProperty(json, "files") && resolutionStack.length === 0) { + errors.push( + createCompilerDiagnostic( + Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + configFileName || "tsconfig.json", + JSON.stringify(includeSpecs || []), + JSON.stringify(excludeSpecs || []))); + } + + return result; } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 21f04da488d..0c92f0acd7f 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3077,6 +3077,7 @@ "category": "Error", "code": 17010 }, + "Circularity detected while resolving configuration: {0}": { "category": "Error", "code": 18000 @@ -3085,6 +3086,15 @@ "category": "Error", "code": 18001 }, + "The 'files' list in config file '{0}' is empty.": { + "category": "Error", + "code": 18002 + }, + "No inputs were found in config file '{0}'. Specified 'include' paths were '{1}' and 'exclude' paths were '{2}'.": { + "category": "Error", + "code": 18003 + }, + "Add missing 'super()' call.": { "category": "Message", "code": 90001 diff --git a/src/harness/unittests/matchFiles.ts b/src/harness/unittests/matchFiles.ts index 6b499e56989..79e9668d073 100644 --- a/src/harness/unittests/matchFiles.ts +++ b/src/harness/unittests/matchFiles.ts @@ -3,6 +3,7 @@ namespace ts { const caseInsensitiveBasePath = "c:/dev/"; + const caseInsensitiveTsconfigPath = "c:/dev/tsconfig.json"; const caseInsensitiveHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, [ "c:/dev/a.ts", "c:/dev/a.d.ts", @@ -88,6 +89,8 @@ namespace ts { "c:/dev/g.min.js/.g/g.ts" ]); + const defaultExcludes = ["node_modules", "bower_components", "jspm_packages"]; + describe("matchFiles", () => { describe("with literal file list", () => { it("without exclusions", () => { @@ -189,11 +192,14 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) + ], fileNames: [], wildcardDirectories: {}, }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -207,11 +213,14 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) + ], fileNames: [], wildcardDirectories: {}, }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -551,13 +560,16 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) + ], fileNames: [], wildcardDirectories: { "c:/dev": ts.WatchDirectoryFlags.Recursive }, }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -619,7 +631,7 @@ namespace ts { it("with common package folders and no exclusions", () => { const json = { include: [ - "**/a.ts" + "**/a.ts" ] }; const expected: ts.ParsedCommandLine = { @@ -701,13 +713,16 @@ namespace ts { options: { allowJs: false }, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) + ], fileNames: [], wildcardDirectories: { "c:/dev/js": ts.WatchDirectoryFlags.None } }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -828,11 +843,14 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(json.exclude))] + , fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -1030,12 +1048,14 @@ namespace ts { const expected: ts.ParsedCommandLine = { options: {}, errors: [ - ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**") + ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**"), + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) ], fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -1051,11 +1071,14 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(json.exclude)) + ], fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -1071,12 +1094,14 @@ namespace ts { const expected: ts.ParsedCommandLine = { options: {}, errors: [ - ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, "**/x/**/*") + ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, "**/x/**/*"), + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) ], fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -1122,12 +1147,14 @@ namespace ts { const expected: ts.ParsedCommandLine = { options: {}, errors: [ - ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/../*") + ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/../*"), + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) ], fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -1142,12 +1169,14 @@ namespace ts { const expected: ts.ParsedCommandLine = { options: {}, errors: [ - ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/../*") + ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/../*"), + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(defaultExcludes)) ], fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); @@ -1195,7 +1224,7 @@ namespace ts { const expected: ts.ParsedCommandLine = { options: {}, errors: [ - ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/..") + ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/..") ], fileNames: [ "c:/dev/a.ts", @@ -1320,11 +1349,14 @@ namespace ts { }; const expected: ts.ParsedCommandLine = { options: {}, - errors: [], + errors: [ + ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, + caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(json.exclude)) + ], fileNames: [], wildcardDirectories: {} }; - const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath); + const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath); assert.deepEqual(actual.fileNames, expected.fileNames); assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories); assert.deepEqual(actual.errors, expected.errors); diff --git a/src/harness/unittests/tsconfigParsing.ts b/src/harness/unittests/tsconfigParsing.ts index f38a05b9cfa..7b980cdd2d8 100644 --- a/src/harness/unittests/tsconfigParsing.ts +++ b/src/harness/unittests/tsconfigParsing.ts @@ -28,6 +28,14 @@ namespace ts { assert.isTrue(arrayIsEqualTo(parsed.fileNames.sort(), expectedFileList.sort())); } + function assertParseFileDiagnostics(jsonText: string, configFileName: string, basePath: string, allFileList: string[], expectedDiagnosticCode: number) { + const json = JSON.parse(jsonText); + const host: ParseConfigHost = new Utils.MockParseConfigHost(basePath, true, allFileList); + const parsed = ts.parseJsonConfigFileContent(json, host, basePath, /*existingOptions*/ undefined, configFileName); + assert.isTrue(parsed.errors.length >= 0); + assert.isTrue(parsed.errors.filter(e => e.code === expectedDiagnosticCode).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)}`); + } + it("returns empty config for file with only whitespaces", () => { assertParseResult("", { config : {} }); assertParseResult(" ", { config : {} }); @@ -202,5 +210,64 @@ namespace ts { assert.isTrue(diagnostics.length === 2); assert.equal(JSON.stringify(configJsonObject), JSON.stringify(expectedResult)); }); + + it("generates errors for empty files list", () => { + const content = `{ + "files": [] + }`; + assertParseFileDiagnostics(content, + "/apath/tsconfig.json", + "tests/cases/unittests", + ["/apath/a.ts"], + Diagnostics.The_files_list_in_config_file_0_is_empty.code); + }); + + it("generates errors for directory with no .ts files", () => { + const content = `{ + }`; + assertParseFileDiagnostics(content, + "/apath/tsconfig.json", + "tests/cases/unittests", + ["/apath/a.js"], + Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code); + }); + + it("generates errors for empty directory", () => { + const content = `{ + "compilerOptions": { + "allowJs": true + } + }`; + assertParseFileDiagnostics(content, + "/apath/tsconfig.json", + "tests/cases/unittests", + [], + Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code); + }); + + it("generates errors for empty include", () => { + const content = `{ + "include": [] + }`; + assertParseFileDiagnostics(content, + "/apath/tsconfig.json", + "tests/cases/unittests", + ["/apath/a.ts"], + Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code); + }); + + it("generates errors for includes with outDir", () => { + const content = `{ + "compilerOptions": { + "outDir": "./" + }, + "include": ["**/*"] + }`; + assertParseFileDiagnostics(content, + "/apath/tsconfig.json", + "tests/cases/unittests", + ["/apath/a.ts"], + Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code); + }); }); }