1namespace ts { 2 describe("unittests:: config:: commandLineParsing:: parseCommandLine", () => { 3 function assertParseResult(subScenario: string, commandLine: string[], workerDiagnostic?: () => ParseCommandLineWorkerDiagnostics) { 4 it(subScenario, () => { 5 const baseline: string[] = []; 6 baseline.push(commandLine.join(" ")); 7 const parsed = parseCommandLineWorker(workerDiagnostic?.() || compilerOptionsDidYouMeanDiagnostics, commandLine); 8 baseline.push("CompilerOptions::"); 9 baseline.push(JSON.stringify(parsed.options, /*replacer*/ undefined, " ")); 10 baseline.push("WatchOptions::"); 11 baseline.push(JSON.stringify(parsed.watchOptions, /*replacer*/ undefined, " ")); 12 baseline.push("FileNames::"); 13 baseline.push(parsed.fileNames.join()); 14 baseline.push("Errors::"); 15 baseline.push(formatDiagnostics(parsed.errors, { 16 getCurrentDirectory: () => "/", 17 getCanonicalFileName: identity, 18 getNewLine: () => "\n", 19 })); 20 Harness.Baseline.runBaseline(`config/commandLineParsing/parseCommandLine/${subScenario}.js`, baseline.join("\n")); 21 }); 22 } 23 24 // --lib es6 0.ts 25 assertParseResult("Parse single option of library flag", ["--lib", "es6", "0.ts"]); 26 assertParseResult("Handles may only be used with --build flags", ["--clean", "--dry", "--force", "--verbose"]); 27 // --declarations --allowTS 28 assertParseResult("Handles did you mean for misspelt flags", ["--declarations", "--allowTS"]); 29 // --lib es5,es2015.symbol.wellknown 0.ts 30 assertParseResult("Parse multiple options of library flags", ["--lib", "es5,es2015.symbol.wellknown", "0.ts"]); 31 // --lib es5,invalidOption 0.ts 32 assertParseResult("Parse invalid option of library flags", ["--lib", "es5,invalidOption", "0.ts"]); 33 // 0.ts --jsx 34 assertParseResult("Parse empty options of --jsx", ["0.ts", "--jsx"]); 35 // 0.ts -- 36 assertParseResult("Parse empty options of --module", ["0.ts", "--module"]); 37 // 0.ts --newLine 38 assertParseResult("Parse empty options of --newLine", ["0.ts", "--newLine"]); 39 // 0.ts --target 40 assertParseResult("Parse empty options of --target", ["0.ts", "--target"]); 41 // 0.ts --moduleResolution 42 assertParseResult("Parse empty options of --moduleResolution", ["0.ts", "--moduleResolution"]); 43 // 0.ts --lib 44 assertParseResult("Parse empty options of --lib", ["0.ts", "--lib"]); 45 // 0.ts --lib 46 // This test is an error because the empty string is falsey 47 assertParseResult("Parse empty string of --lib", ["0.ts", "--lib", ""]); 48 // 0.ts --lib 49 assertParseResult("Parse immediately following command line argument of --lib", ["0.ts", "--lib", "--sourcemap"]); 50 // --lib es5, es7 0.ts 51 assertParseResult("Parse --lib option with extra comma", ["--lib", "es5,", "es7", "0.ts"]); 52 // --lib es5, es7 0.ts 53 assertParseResult("Parse --lib option with trailing white-space", ["--lib", "es5, ", "es7", "0.ts"]); 54 // --lib es5,es2015.symbol.wellknown --target es5 0.ts 55 assertParseResult("Parse multiple compiler flags with input files at the end", ["--lib", "es5,es2015.symbol.wellknown", "--target", "es5", "0.ts"]); 56 // --module commonjs --target es5 0.ts --lib es5,es2015.symbol.wellknown 57 assertParseResult("Parse multiple compiler flags with input files in the middle", ["--module", "commonjs", "--target", "es5", "0.ts", "--lib", "es5,es2015.symbol.wellknown"]); 58 // --module commonjs --target es5 --lib es5 0.ts --library es2015.array,es2015.symbol.wellknown 59 assertParseResult("Parse multiple library compiler flags ", ["--module", "commonjs", "--target", "es5", "--lib", "es5", "0.ts", "--lib", "es2015.core, es2015.symbol.wellknown "]); 60 assertParseResult("Parse explicit boolean flag value", ["--strictNullChecks", "false", "0.ts"]); 61 assertParseResult("Parse non boolean argument after boolean flag", ["--noImplicitAny", "t", "0.ts"]); 62 assertParseResult("Parse implicit boolean flag value", ["--strictNullChecks"]); 63 assertParseResult("parse --incremental", ["--incremental", "0.ts"]); 64 assertParseResult("parse --tsBuildInfoFile", ["--tsBuildInfoFile", "build.tsbuildinfo", "0.ts"]); 65 66 describe("parses command line null for tsconfig only option", () => { 67 interface VerifyNull { 68 subScenario: string, 69 optionName: string; 70 nonNullValue?: string; 71 workerDiagnostic?: () => ParseCommandLineWorkerDiagnostics; 72 } 73 function verifyNull({ subScenario, optionName, nonNullValue, workerDiagnostic }: VerifyNull) { 74 describe(subScenario, () => { 75 assertParseResult( 76 `${subScenario} allows setting it to null`, 77 [`--${optionName}`, "null", "0.ts"], 78 workerDiagnostic 79 ); 80 if (nonNullValue) { 81 assertParseResult( 82 `${subScenario} errors if non null value is passed`, 83 [`--${optionName}`, nonNullValue, "0.ts"], 84 workerDiagnostic 85 ); 86 } 87 88 assertParseResult( 89 `${subScenario} errors if its followed by another option`, 90 ["0.ts", "--strictNullChecks", `--${optionName}`], 91 workerDiagnostic 92 ); 93 94 assertParseResult( 95 `${subScenario} errors if its last option`, 96 ["0.ts", `--${optionName}`], 97 workerDiagnostic 98 ); 99 }); 100 } 101 102 interface VerifyNullNonIncludedOption { 103 subScenario: string, 104 type: () => "string" | "number" | ESMap<string, number | string>; 105 nonNullValue?: string; 106 } 107 function verifyNullNonIncludedOption({ subScenario, type, nonNullValue }: VerifyNullNonIncludedOption) { 108 verifyNull({ 109 subScenario, 110 optionName: "optionName", 111 nonNullValue, 112 workerDiagnostic: () => { 113 const optionDeclarations: CommandLineOption[] = [ 114 ...compilerOptionsDidYouMeanDiagnostics.optionDeclarations, 115 { 116 name: "optionName", 117 type: type(), 118 isTSConfigOnly: true, 119 category: Diagnostics.Backwards_Compatibility, 120 description: Diagnostics.Enable_project_compilation, 121 defaultValueDescription: undefined, 122 } 123 ]; 124 return { 125 ...compilerOptionsDidYouMeanDiagnostics, 126 optionDeclarations, 127 getOptionsNameMap: () => createOptionNameMap(optionDeclarations) 128 }; 129 } 130 }); 131 } 132 133 describe("option of type boolean", () => { 134 assertParseResult( 135 "allows setting option type boolean to false", 136 ["--composite", "false", "0.ts"], 137 ); 138 139 verifyNull({ 140 subScenario: "option of type boolean", 141 optionName: "composite", 142 nonNullValue: "true", 143 }); 144 }); 145 146 verifyNull({ 147 subScenario: "option of type object", 148 optionName: "paths", 149 }); 150 151 verifyNull({ 152 subScenario: "option of type list", 153 optionName: "rootDirs", 154 nonNullValue: "abc,xyz", 155 }); 156 verifyNullNonIncludedOption({ 157 subScenario: "option of type string", 158 type: () => "string", 159 nonNullValue: "hello" 160 }); 161 162 verifyNullNonIncludedOption({ 163 subScenario: "option of type number", 164 type: () => "number", 165 nonNullValue: "10" 166 }); 167 168 verifyNullNonIncludedOption({ 169 subScenario: "option of type custom map", 170 type: () => new Map(getEntries({ 171 node: ModuleResolutionKind.NodeJs, 172 classic: ModuleResolutionKind.Classic, 173 })), 174 nonNullValue: "node" 175 }); 176 }); 177 178 assertParseResult("allows tsconfig only option to be set to null", ["--composite", "null", "-tsBuildInfoFile", "null", "0.ts"]); 179 180 describe("Watch options", () => { 181 assertParseResult("parse --watchFile", ["--watchFile", "UseFsEvents", "0.ts"]); 182 assertParseResult("parse --watchDirectory", ["--watchDirectory", "FixedPollingInterval", "0.ts"]); 183 assertParseResult("parse --fallbackPolling", ["--fallbackPolling", "PriorityInterval", "0.ts"]); 184 assertParseResult("parse --synchronousWatchDirectory", ["--synchronousWatchDirectory", "0.ts"]); 185 assertParseResult("errors on missing argument to --fallbackPolling", ["0.ts", "--fallbackPolling"]); 186 assertParseResult("parse --excludeDirectories", ["--excludeDirectories", "**/temp", "0.ts"]); 187 assertParseResult("errors on invalid excludeDirectories", ["--excludeDirectories", "**/../*", "0.ts"]); 188 assertParseResult("parse --excludeFiles", ["--excludeFiles", "**/temp/*.ts", "0.ts"]); 189 assertParseResult("errors on invalid excludeFiles", ["--excludeFiles", "**/../*", "0.ts"]); 190 }); 191 }); 192 193 describe("unittests:: config:: commandLineParsing:: parseBuildOptions", () => { 194 function assertParseResult(subScenario: string, commandLine: string[]) { 195 it(subScenario, () => { 196 const baseline: string[] = []; 197 baseline.push(commandLine.join(" ")); 198 const parsed = parseBuildCommand(commandLine); 199 baseline.push("buildOptions::"); 200 baseline.push(JSON.stringify(parsed.buildOptions, /*replacer*/ undefined, " ")); 201 baseline.push("WatchOptions::"); 202 baseline.push(JSON.stringify(parsed.watchOptions, /*replacer*/ undefined, " ")); 203 baseline.push("Projects::"); 204 baseline.push(parsed.projects.join()); 205 baseline.push("Errors::"); 206 baseline.push(formatDiagnostics(parsed.errors, { 207 getCurrentDirectory: () => "/", 208 getCanonicalFileName: identity, 209 getNewLine: () => "\n", 210 })); 211 Harness.Baseline.runBaseline(`config/commandLineParsing/parseBuildOptions/${subScenario}.js`, baseline.join("\n")); 212 }); 213 } 214 assertParseResult("parse build without any options ", []); 215 assertParseResult("Parse multiple options", ["--verbose", "--force", "tests"]); 216 assertParseResult("Parse option with invalid option", ["--verbose", "--invalidOption"]); 217 assertParseResult("Parse multiple flags with input projects at the end", ["--force", "--verbose", "src", "tests"]); 218 assertParseResult("Parse multiple flags with input projects in the middle", ["--force", "src", "tests", "--verbose"]); 219 assertParseResult("Parse multiple flags with input projects in the beginning", ["src", "tests", "--force", "--verbose"]); 220 assertParseResult("parse build with --incremental", ["--incremental", "tests"]); 221 assertParseResult("parse build with --locale en-us", ["--locale", "en-us", "src"]); 222 assertParseResult("parse build with --tsBuildInfoFile", ["--tsBuildInfoFile", "build.tsbuildinfo", "tests"]); 223 assertParseResult("reports other common may not be used with --build flags", ["--declaration", "--strict"]); 224 225 describe("Combining options that make no sense together", () => { 226 function verifyInvalidCombination(flag1: keyof BuildOptions, flag2: keyof BuildOptions) { 227 assertParseResult(`--${flag1} and --${flag2} together is invalid`, [`--${flag1}`, `--${flag2}`]); 228 } 229 verifyInvalidCombination("clean", "force"); 230 verifyInvalidCombination("clean", "verbose"); 231 verifyInvalidCombination("clean", "watch"); 232 verifyInvalidCombination("watch", "dry"); 233 }); 234 235 describe("Watch options", () => { 236 assertParseResult("parse --watchFile", ["--watchFile", "UseFsEvents", "--verbose"]); 237 assertParseResult("parse --watchDirectory", ["--watchDirectory", "FixedPollingInterval", "--verbose"]); 238 assertParseResult("parse --fallbackPolling", ["--fallbackPolling", "PriorityInterval", "--verbose"]); 239 assertParseResult("parse --synchronousWatchDirectory", ["--synchronousWatchDirectory", "--verbose"]); 240 assertParseResult("errors on missing argument", ["--verbose", "--fallbackPolling"]); 241 assertParseResult("errors on invalid excludeDirectories", ["--excludeDirectories", "**/../*"]); 242 assertParseResult("parse --excludeFiles", ["--excludeFiles", "**/temp/*.ts"]); 243 assertParseResult("errors on invalid excludeFiles", ["--excludeFiles", "**/../*"]); 244 }); 245 }); 246} 247