diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 596f8651500..e4c30cdabe8 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1227,7 +1227,7 @@ namespace ts { } /*@internal*/ - export function parseBuildCommand(args: string[]): ParsedBuildCommand { + export function parseBuildCommand(args: readonly string[]): ParsedBuildCommand { let buildOptionNameMap: OptionNameMap | undefined; const returnBuildOptionNameMap = () => (buildOptionNameMap || (buildOptionNameMap = createOptionNameMap(buildOpts))); const { options, fileNames: projects, errors } = parseCommandLineWorker(returnBuildOptionNameMap, [ @@ -1258,125 +1258,12 @@ namespace ts { return { buildOptions, projects, errors }; } - function getDiagnosticText(_message: DiagnosticMessage, ..._args: any[]): string { + /* @internal */ + export function getDiagnosticText(_message: DiagnosticMessage, ..._args: any[]): string { const diagnostic = createCompilerDiagnostic.apply(undefined, arguments); return diagnostic.messageText; } - /* @internal */ - export function printVersion() { - sys.write(getDiagnosticText(Diagnostics.Version_0, version) + sys.newLine); - } - - /* @internal */ - export function printHelp(optionsList: readonly CommandLineOption[], syntaxPrefix = "") { - const output: string[] = []; - - // We want to align our "syntax" and "examples" commands to a certain margin. - const syntaxLength = getDiagnosticText(Diagnostics.Syntax_Colon_0, "").length; - const examplesLength = getDiagnosticText(Diagnostics.Examples_Colon_0, "").length; - let marginLength = Math.max(syntaxLength, examplesLength); - - // Build up the syntactic skeleton. - let syntax = makePadding(marginLength - syntaxLength); - syntax += `tsc ${syntaxPrefix}[${getDiagnosticText(Diagnostics.options)}] [${getDiagnosticText(Diagnostics.file)}...]`; - - output.push(getDiagnosticText(Diagnostics.Syntax_Colon_0, syntax)); - output.push(sys.newLine + sys.newLine); - - // Build up the list of examples. - const padding = makePadding(marginLength); - output.push(getDiagnosticText(Diagnostics.Examples_Colon_0, makePadding(marginLength - examplesLength) + "tsc hello.ts") + sys.newLine); - output.push(padding + "tsc --outFile file.js file.ts" + sys.newLine); - output.push(padding + "tsc @args.txt" + sys.newLine); - output.push(padding + "tsc --build tsconfig.json" + sys.newLine); - output.push(sys.newLine); - - output.push(getDiagnosticText(Diagnostics.Options_Colon) + sys.newLine); - - // We want our descriptions to align at the same column in our output, - // so we keep track of the longest option usage string. - marginLength = 0; - const usageColumn: string[] = []; // Things like "-d, --declaration" go in here. - const descriptionColumn: string[] = []; - - const optionsDescriptionMap = createMap(); // Map between option.description and list of option.type if it is a kind - - for (const option of optionsList) { - // If an option lacks a description, - // it is not officially supported. - if (!option.description) { - continue; - } - - let usageText = " "; - if (option.shortName) { - usageText += "-" + option.shortName; - usageText += getParamType(option); - usageText += ", "; - } - - usageText += "--" + option.name; - usageText += getParamType(option); - - usageColumn.push(usageText); - let description: string; - - if (option.name === "lib") { - description = getDiagnosticText(option.description); - const element = (option).element; - const typeMap = >element.type; - optionsDescriptionMap.set(description, arrayFrom(typeMap.keys()).map(key => `'${key}'`)); - } - else { - description = getDiagnosticText(option.description); - } - - descriptionColumn.push(description); - - // Set the new margin for the description column if necessary. - marginLength = Math.max(usageText.length, marginLength); - } - - // Special case that can't fit in the loop. - const usageText = " @<" + getDiagnosticText(Diagnostics.file) + ">"; - usageColumn.push(usageText); - descriptionColumn.push(getDiagnosticText(Diagnostics.Insert_command_line_options_and_files_from_a_file)); - marginLength = Math.max(usageText.length, marginLength); - - // Print out each row, aligning all the descriptions on the same column. - for (let i = 0; i < usageColumn.length; i++) { - const usage = usageColumn[i]; - const description = descriptionColumn[i]; - const kindsList = optionsDescriptionMap.get(description); - output.push(usage + makePadding(marginLength - usage.length + 2) + description + sys.newLine); - - if (kindsList) { - output.push(makePadding(marginLength + 4)); - for (const kind of kindsList) { - output.push(kind + " "); - } - output.push(sys.newLine); - } - } - - for (const line of output) { - sys.write(line); - } - return; - - function getParamType(option: CommandLineOption) { - if (option.paramType !== undefined) { - return " " + getDiagnosticText(option.paramType); - } - return ""; - } - - function makePadding(paddingLength: number): string { - return Array(paddingLength + 1).join(" "); - } - } - export type DiagnosticReporter = (diagnostic: Diagnostic) => void; /** * Reports config file diagnostics @@ -1801,6 +1688,12 @@ namespace ts { references: readonly ProjectReference[] | undefined; } + /** @internal */ + export interface ConvertToTSConfigHost { + getCurrentDirectory(): string; + useCaseSensitiveFileNames: boolean; + } + /** * Generate an uncommented, complete tsconfig for use with "--showConfig" * @param configParseResult options to be generated into tsconfig.json @@ -1808,7 +1701,7 @@ namespace ts { * @param host provides current directory and case sensitivity services */ /** @internal */ - export function convertToTSConfig(configParseResult: ParsedCommandLine, configFileName: string, host: { getCurrentDirectory(): string, useCaseSensitiveFileNames: boolean }): TSConfig { + export function convertToTSConfig(configParseResult: ParsedCommandLine, configFileName: string, host: ConvertToTSConfigHost): TSConfig { const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); const files = map( filter( @@ -1816,7 +1709,8 @@ namespace ts { (!configParseResult.configFileSpecs || !configParseResult.configFileSpecs.validatedIncludeSpecs) ? _ => true : matchesSpecs( configFileName, configParseResult.configFileSpecs.validatedIncludeSpecs, - configParseResult.configFileSpecs.validatedExcludeSpecs + configParseResult.configFileSpecs.validatedExcludeSpecs, + host, ) ), f => getRelativePathFromFile(getNormalizedAbsolutePath(configFileName, host.getCurrentDirectory()), getNormalizedAbsolutePath(f, host.getCurrentDirectory()), getCanonicalFileName) @@ -1854,11 +1748,11 @@ namespace ts { return specs; } - function matchesSpecs(path: string, includeSpecs: readonly string[] | undefined, excludeSpecs: readonly string[] | undefined): (path: string) => boolean { + function matchesSpecs(path: string, includeSpecs: readonly string[] | undefined, excludeSpecs: readonly string[] | undefined, host: ConvertToTSConfigHost): (path: string) => boolean { if (!includeSpecs) return _ => true; - const patterns = getFileMatcherPatterns(path, excludeSpecs, includeSpecs, sys.useCaseSensitiveFileNames, sys.getCurrentDirectory()); - const excludeRe = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, sys.useCaseSensitiveFileNames); - const includeRe = patterns.includeFilePattern && getRegexFromPattern(patterns.includeFilePattern, sys.useCaseSensitiveFileNames); + const patterns = getFileMatcherPatterns(path, excludeSpecs, includeSpecs, host.useCaseSensitiveFileNames, host.getCurrentDirectory()); + const excludeRe = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, host.useCaseSensitiveFileNames); + const includeRe = patterns.includeFilePattern && getRegexFromPattern(patterns.includeFilePattern, host.useCaseSensitiveFileNames); if (includeRe) { if (excludeRe) { return path => !(includeRe.test(path) && !excludeRe.test(path)); diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 06871fc9cac..cac3f3009b1 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -91,7 +91,7 @@ namespace ts { /** Parses config file using System interface */ export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter) { const host: ParseConfigFileHost = system; - host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(sys, reportDiagnostic, diagnostic); + host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic); const result = getParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host); host.onUnRecoverableConfigFileDiagnostic = undefined!; // TODO: GH#18217 return result; diff --git a/src/testRunner/tsconfig.json b/src/testRunner/tsconfig.json index 6eec3a90221..15beae61f25 100644 --- a/src/testRunner/tsconfig.json +++ b/src/testRunner/tsconfig.json @@ -39,6 +39,7 @@ "unittests/services/extract/helpers.ts", "unittests/tsbuild/helpers.ts", + "../tsc/executeCommandLine.ts", "unittests/tsc/helpers.ts", "unittests/tscWatch/helpers.ts", "unittests/tsserver/helpers.ts", diff --git a/src/testRunner/unittests/tsc/helpers.ts b/src/testRunner/unittests/tsc/helpers.ts index 80a14705390..6de4a4f08b6 100644 --- a/src/testRunner/unittests/tsc/helpers.ts +++ b/src/testRunner/unittests/tsc/helpers.ts @@ -3,167 +3,6 @@ namespace ts { writtenFiles: Map; baseLine(): void; }; - function executeCommandLine(sys: TscCompileSystem, commandLineArgs: readonly string[]) { - if (isBuild(commandLineArgs)) { - return performBuild(sys, commandLineArgs.slice(1)); - } - - const reportDiagnostic = createDiagnosticReporter(sys); - const commandLine = parseCommandLine(commandLineArgs, path => sys.readFile(path)); - if (commandLine.options.build) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_build_must_be_the_first_command_line_argument)); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - - if (commandLine.errors.length > 0) { - commandLine.errors.forEach(reportDiagnostic); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - - let configFileName: string | undefined; - if (commandLine.options.project) { - if (commandLine.fileNames.length !== 0) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_project_cannot_be_mixed_with_source_files_on_a_command_line)); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - - const fileOrDirectory = normalizePath(commandLine.options.project); - if (!fileOrDirectory /* current directory "." */ || sys.directoryExists(fileOrDirectory)) { - configFileName = combinePaths(fileOrDirectory, "tsconfig.json"); - if (!sys.fileExists(configFileName)) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.Cannot_find_a_tsconfig_json_file_at_the_specified_directory_Colon_0, commandLine.options.project)); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - } - else { - configFileName = fileOrDirectory; - if (!sys.fileExists(configFileName)) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_specified_path_does_not_exist_Colon_0, commandLine.options.project)); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - } - } - else if (commandLine.fileNames.length === 0) { - const searchPath = normalizePath(sys.getCurrentDirectory()); - configFileName = findConfigFile(searchPath, sys.fileExists); - } - - Debug.assert(commandLine.fileNames.length !== 0 || !!configFileName); - - const currentDirectory = sys.getCurrentDirectory(); - const getCanonicalFileName = createGetCanonicalFileName(sys.useCaseSensitiveFileNames); - const commandLineOptions = convertToOptionsWithAbsolutePaths( - commandLine.options, - fileName => toPath(fileName, currentDirectory, getCanonicalFileName) - ); - - if (configFileName) { - const configParseResult = Debug.assertDefined(parseConfigFileWithSystem(configFileName, commandLineOptions, sys, reportDiagnostic)); - if (isIncrementalCompilation(configParseResult.options)) { - performIncrementalCompilation(sys, configParseResult); - } - else { - performCompilation(sys, configParseResult); - } - } - else { - if (isIncrementalCompilation(commandLine.options)) { - performIncrementalCompilation(sys, { - ...commandLine, - options: commandLineOptions - }); - } - else { - performCompilation(sys, { - ...commandLine, - options: commandLineOptions - }); - } - } - } - - function createReportErrorSummary(sys: TscCompileSystem, options: CompilerOptions): ReportEmitErrorSummary | undefined { - return options.pretty ? - errorCount => sys.write(getErrorSummaryText(errorCount, sys.newLine)) : - undefined; - } - - function performCompilation(sys: TscCompileSystem, config: ParsedCommandLine) { - const { fileNames, options, projectReferences } = config; - const reportDiagnostic = createDiagnosticReporter(sys, options.pretty); - const host = createCompilerHostWorker(options, /*setParentPos*/ undefined, sys); - fakes.patchHostForBuildInfoReadWrite(host); - const currentDirectory = host.getCurrentDirectory(); - const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames()); - changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, currentDirectory, getCanonicalFileName)); - const program = createProgram({ - rootNames: fileNames, - options, - projectReferences, - host, - configFileParsingDiagnostics: getConfigFileParsingDiagnostics(config) - }); - const exitStatus = emitFilesAndReportErrorsAndGetExitStatus( - program, - reportDiagnostic, - s => sys.write(s + sys.newLine), - createReportErrorSummary(sys, options) - ); - baselineBuildInfo([config], sys.vfs, sys.writtenFiles); - return sys.exit(exitStatus); - } - - function performIncrementalCompilation(sys: TscCompileSystem, config: ParsedCommandLine) { - const reportDiagnostic = createDiagnosticReporter(sys, config.options.pretty); - const { options, fileNames, projectReferences } = config; - const host = createIncrementalCompilerHost(options, sys); - fakes.patchHostForBuildInfoReadWrite(host); - const exitCode = ts.performIncrementalCompilation({ - host, - system: sys, - rootNames: fileNames, - options, - configFileParsingDiagnostics: getConfigFileParsingDiagnostics(config), - projectReferences, - reportDiagnostic, - reportErrorSummary: createReportErrorSummary(sys, options), - }); - baselineBuildInfo([config], sys.vfs, sys.writtenFiles); - return sys.exit(exitCode); - } - - function performBuild(sys: TscCompileSystem, args: string[]) { - const { buildOptions, projects, errors } = parseBuildCommand(args); - const reportDiagnostic = createDiagnosticReporter(sys, buildOptions.pretty); - - if (errors.length > 0) { - errors.forEach(reportDiagnostic); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - - Debug.assert(projects.length !== 0); - - const buildHost = createSolutionBuilderHost( - sys, - /*createProgram*/ undefined, - reportDiagnostic, - createBuilderStatusReporter(sys, buildOptions.pretty), - createReportErrorSummary(sys, buildOptions) - ); - fakes.patchSolutionBuilderHost(buildHost, sys); - const builder = createSolutionBuilder(buildHost, projects, buildOptions); - const exitCode = buildOptions.clean ? builder.clean() : builder.build(); - baselineBuildInfo(builder.getAllParsedConfigs(), sys.vfs, sys.writtenFiles); - return sys.exit(exitCode); - } - - function isBuild(commandLineArgs: readonly string[]) { - if (commandLineArgs.length > 0 && commandLineArgs[0].charCodeAt(0) === CharacterCodes.minus) { - const firstOption = commandLineArgs[0].slice(commandLineArgs[0].charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase(); - return firstOption === "build" || firstOption === "b"; - } - return false; - } export enum BuildKind { Initial = "initial-build", @@ -221,7 +60,16 @@ namespace ts { sys.write(`${sys.getExecutingFilePath()} ${commandLineArgs.join(" ")}\n`); sys.exit = exitCode => sys.exitCode = exitCode; - executeCommandLine(sys, commandLineArgs); + ts.executeCommandLine( + sys, + { + onCompilerHostCreate: host => fakes.patchHostForBuildInfoReadWrite(host), + onCompilationComplete: config => baselineBuildInfo([config], sys.vfs, sys.writtenFiles), + onSolutionBuilderHostCreate: host => fakes.patchSolutionBuilderHost(host, sys), + onSolutionBuildComplete: configs => baselineBuildInfo(configs, sys.vfs, sys.writtenFiles), + }, + commandLineArgs, + ); sys.write(`exitCode:: ExitStatus.${ExitStatus[sys.exitCode as ExitStatus]}\n`); if (baselineReadFileCalls) { sys.write(`readFiles:: ${JSON.stringify(actualReadFileMap, /*replacer*/ undefined, " ")} `); diff --git a/src/tsc/executeCommandLine.ts b/src/tsc/executeCommandLine.ts new file mode 100644 index 00000000000..1496ac39931 --- /dev/null +++ b/src/tsc/executeCommandLine.ts @@ -0,0 +1,725 @@ +namespace ts { + interface Statistic { + name: string; + value: string; + } + + function countLines(program: Program): number { + let count = 0; + forEach(program.getSourceFiles(), file => { + count += getLineStarts(file).length; + }); + return count; + } + + function updateReportDiagnostic( + sys: System, + existing: DiagnosticReporter, + options: CompilerOptions | BuildOptions + ): DiagnosticReporter { + return shouldBePretty(sys, options) ? + createDiagnosticReporter(sys, /*pretty*/ true) : + existing; + } + + function defaultIsPretty(sys: System) { + return !!sys.writeOutputIsTTY && sys.writeOutputIsTTY(); + } + + function shouldBePretty(sys: System, options: CompilerOptions | BuildOptions) { + if (!options || typeof options.pretty === "undefined") { + return defaultIsPretty(sys); + } + return options.pretty; + } + + function padLeft(s: string, length: number) { + while (s.length < length) { + s = " " + s; + } + return s; + } + + function padRight(s: string, length: number) { + while (s.length < length) { + s = s + " "; + } + + return s; + } + + function getOptionsForHelp(commandLine: ParsedCommandLine) { + // Sort our options by their names, (e.g. "--noImplicitAny" comes before "--watch") + return !!commandLine.options.all ? + sort(optionDeclarations, (a, b) => compareStringsCaseInsensitive(a.name, b.name)) : + filter(optionDeclarations.slice(), v => !!v.showInSimplifiedHelpView); + } + + function printVersion(sys: System) { + sys.write(getDiagnosticText(Diagnostics.Version_0, version) + sys.newLine); + } + + function printHelp(sys: System, optionsList: readonly CommandLineOption[], syntaxPrefix = "") { + const output: string[] = []; + + // We want to align our "syntax" and "examples" commands to a certain margin. + const syntaxLength = getDiagnosticText(Diagnostics.Syntax_Colon_0, "").length; + const examplesLength = getDiagnosticText(Diagnostics.Examples_Colon_0, "").length; + let marginLength = Math.max(syntaxLength, examplesLength); + + // Build up the syntactic skeleton. + let syntax = makePadding(marginLength - syntaxLength); + syntax += `tsc ${syntaxPrefix}[${getDiagnosticText(Diagnostics.options)}] [${getDiagnosticText(Diagnostics.file)}...]`; + + output.push(getDiagnosticText(Diagnostics.Syntax_Colon_0, syntax)); + output.push(sys.newLine + sys.newLine); + + // Build up the list of examples. + const padding = makePadding(marginLength); + output.push(getDiagnosticText(Diagnostics.Examples_Colon_0, makePadding(marginLength - examplesLength) + "tsc hello.ts") + sys.newLine); + output.push(padding + "tsc --outFile file.js file.ts" + sys.newLine); + output.push(padding + "tsc @args.txt" + sys.newLine); + output.push(padding + "tsc --build tsconfig.json" + sys.newLine); + output.push(sys.newLine); + + output.push(getDiagnosticText(Diagnostics.Options_Colon) + sys.newLine); + + // We want our descriptions to align at the same column in our output, + // so we keep track of the longest option usage string. + marginLength = 0; + const usageColumn: string[] = []; // Things like "-d, --declaration" go in here. + const descriptionColumn: string[] = []; + + const optionsDescriptionMap = createMap(); // Map between option.description and list of option.type if it is a kind + + for (const option of optionsList) { + // If an option lacks a description, + // it is not officially supported. + if (!option.description) { + continue; + } + + let usageText = " "; + if (option.shortName) { + usageText += "-" + option.shortName; + usageText += getParamType(option); + usageText += ", "; + } + + usageText += "--" + option.name; + usageText += getParamType(option); + + usageColumn.push(usageText); + let description: string; + + if (option.name === "lib") { + description = getDiagnosticText(option.description); + const element = (option).element; + const typeMap = >element.type; + optionsDescriptionMap.set(description, arrayFrom(typeMap.keys()).map(key => `'${key}'`)); + } + else { + description = getDiagnosticText(option.description); + } + + descriptionColumn.push(description); + + // Set the new margin for the description column if necessary. + marginLength = Math.max(usageText.length, marginLength); + } + + // Special case that can't fit in the loop. + const usageText = " @<" + getDiagnosticText(Diagnostics.file) + ">"; + usageColumn.push(usageText); + descriptionColumn.push(getDiagnosticText(Diagnostics.Insert_command_line_options_and_files_from_a_file)); + marginLength = Math.max(usageText.length, marginLength); + + // Print out each row, aligning all the descriptions on the same column. + for (let i = 0; i < usageColumn.length; i++) { + const usage = usageColumn[i]; + const description = descriptionColumn[i]; + const kindsList = optionsDescriptionMap.get(description); + output.push(usage + makePadding(marginLength - usage.length + 2) + description + sys.newLine); + + if (kindsList) { + output.push(makePadding(marginLength + 4)); + for (const kind of kindsList) { + output.push(kind + " "); + } + output.push(sys.newLine); + } + } + + for (const line of output) { + sys.write(line); + } + return; + + function getParamType(option: CommandLineOption) { + if (option.paramType !== undefined) { + return " " + getDiagnosticText(option.paramType); + } + return ""; + } + + function makePadding(paddingLength: number): string { + return Array(paddingLength + 1).join(" "); + } + } + + function executeCommandLineWorker( + sys: System, + cb: ExecuteCommandLineCallbacks | undefined, + commandLine: ParsedCommandLine, + ) { + let reportDiagnostic = createDiagnosticReporter(sys); + if (commandLine.options.build) { + reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_build_must_be_the_first_command_line_argument)); + return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + } + + // Configuration file name (if any) + let configFileName: string | undefined; + if (commandLine.options.locale) { + validateLocaleAndSetLanguage(commandLine.options.locale, sys, commandLine.errors); + } + + // If there are any errors due to command line parsing and/or + // setting up localization, report them and quit. + if (commandLine.errors.length > 0) { + commandLine.errors.forEach(reportDiagnostic); + return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + } + + if (commandLine.options.init) { + writeConfigFile(sys, reportDiagnostic, commandLine.options, commandLine.fileNames); + return sys.exit(ExitStatus.Success); + } + + if (commandLine.options.version) { + printVersion(sys); + return sys.exit(ExitStatus.Success); + } + + if (commandLine.options.help || commandLine.options.all) { + printVersion(sys); + printHelp(sys, getOptionsForHelp(commandLine)); + return sys.exit(ExitStatus.Success); + } + + if (commandLine.options.project) { + if (commandLine.fileNames.length !== 0) { + reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_project_cannot_be_mixed_with_source_files_on_a_command_line)); + return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + } + + const fileOrDirectory = normalizePath(commandLine.options.project); + if (!fileOrDirectory /* current directory "." */ || sys.directoryExists(fileOrDirectory)) { + configFileName = combinePaths(fileOrDirectory, "tsconfig.json"); + if (!sys.fileExists(configFileName)) { + reportDiagnostic(createCompilerDiagnostic(Diagnostics.Cannot_find_a_tsconfig_json_file_at_the_specified_directory_Colon_0, commandLine.options.project)); + return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + } + } + else { + configFileName = fileOrDirectory; + if (!sys.fileExists(configFileName)) { + reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_specified_path_does_not_exist_Colon_0, commandLine.options.project)); + return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + } + } + } + else if (commandLine.fileNames.length === 0) { + const searchPath = normalizePath(sys.getCurrentDirectory()); + configFileName = findConfigFile(searchPath, sys.fileExists); + } + + if (commandLine.fileNames.length === 0 && !configFileName) { + printVersion(sys); + printHelp(sys, getOptionsForHelp(commandLine)); + return sys.exit(ExitStatus.Success); + } + + const currentDirectory = sys.getCurrentDirectory(); + const getCanonicalFileName = createGetCanonicalFileName(sys.useCaseSensitiveFileNames); + const commandLineOptions = convertToOptionsWithAbsolutePaths( + commandLine.options, + fileName => toPath(fileName, currentDirectory, getCanonicalFileName) + ); + if (configFileName) { + const configParseResult = parseConfigFileWithSystem(configFileName, commandLineOptions, sys, reportDiagnostic)!; // TODO: GH#18217 + if (commandLineOptions.showConfig) { + if (configParseResult.errors.length !== 0) { + reportDiagnostic = updateReportDiagnostic( + sys, + reportDiagnostic, + configParseResult.options + ); + configParseResult.errors.forEach(reportDiagnostic); + return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + } + // eslint-disable-next-line no-null/no-null + sys.write(JSON.stringify(convertToTSConfig(configParseResult, configFileName, sys), null, 4) + sys.newLine); + return sys.exit(ExitStatus.Success); + } + reportDiagnostic = updateReportDiagnostic( + sys, + reportDiagnostic, + configParseResult.options + ); + if (isWatchSet(configParseResult.options)) { + if (reportWatchModeWithoutSysSupport(sys, reportDiagnostic)) return; + createWatchOfConfigFile( + sys, + reportDiagnostic, + configParseResult, + commandLineOptions + ); + } + else if (isIncrementalCompilation(configParseResult.options)) { + performIncrementalCompilation( + sys, + reportDiagnostic, + cb, + configParseResult + ); + } + else { + performCompilation( + sys, + reportDiagnostic, + cb, + configParseResult + ); + } + } + else { + if (commandLineOptions.showConfig) { + // eslint-disable-next-line no-null/no-null + sys.write(JSON.stringify(convertToTSConfig(commandLine, combinePaths(currentDirectory, "tsconfig.json"), sys), null, 4) + sys.newLine); + return sys.exit(ExitStatus.Success); + } + reportDiagnostic = updateReportDiagnostic( + sys, + reportDiagnostic, + commandLineOptions + ); + if (isWatchSet(commandLineOptions)) { + if (reportWatchModeWithoutSysSupport(sys, reportDiagnostic)) return; + createWatchOfFilesAndCompilerOptions( + sys, + reportDiagnostic, + commandLine.fileNames, + commandLineOptions + ); + } + else if (isIncrementalCompilation(commandLineOptions)) { + performIncrementalCompilation( + sys, + reportDiagnostic, + cb, + { ...commandLine, options: commandLineOptions } + ); + } + else { + performCompilation( + sys, + reportDiagnostic, + cb, + { ...commandLine, options: commandLineOptions } + ); + } + } + } + + export function isBuild(commandLineArgs: readonly string[]) { + if (commandLineArgs.length > 0 && commandLineArgs[0].charCodeAt(0) === CharacterCodes.minus) { + const firstOption = commandLineArgs[0].slice(commandLineArgs[0].charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase(); + return firstOption === "build" || firstOption === "b"; + } + return false; + } + + export interface ExecuteCommandLineCallbacks { + onCompilerHostCreate: (host: CompilerHost) => void; + onCompilationComplete: (config: ParsedCommandLine) => void; + onSolutionBuilderHostCreate: (host: SolutionBuilderHost | SolutionBuilderWithWatchHost) => void; + onSolutionBuildComplete: (configs: readonly ParsedCommandLine[]) => void; + } + export function executeCommandLine( + system: System, + cb: ExecuteCommandLineCallbacks, + commandLineArgs: readonly string[], + ): void { + if (isBuild(commandLineArgs)) { + return performBuild( + system, + cb, + commandLineArgs.slice(1) + ); + } + + const commandLine = parseCommandLine(commandLineArgs, path => system.readFile(path)); + if (commandLine.options.generateCpuProfile && system.enableCPUProfiler) { + system.enableCPUProfiler(commandLine.options.generateCpuProfile, () => executeCommandLineWorker( + system, + cb, + commandLine + )); + } + else { + executeCommandLineWorker(system, cb, commandLine); + } + } + + function reportWatchModeWithoutSysSupport(sys: System, reportDiagnostic: DiagnosticReporter) { + if (!sys.watchFile || !sys.watchDirectory) { + reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--watch")); + sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + return true; + } + return false; + } + + function performBuildWorker( + sys: System, + cb: ExecuteCommandLineCallbacks | undefined, + buildOptions: BuildOptions, + projects: string[], + errors: Diagnostic[] + ) { + // Update to pretty if host supports it + const reportDiagnostic = updateReportDiagnostic( + sys, + createDiagnosticReporter(sys), + buildOptions + ); + + if (buildOptions.locale) { + validateLocaleAndSetLanguage(buildOptions.locale, sys, errors); + } + + if (errors.length > 0) { + errors.forEach(reportDiagnostic); + return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + } + + if (buildOptions.help) { + printVersion(sys); + printHelp(sys, buildOpts, "--build "); + return sys.exit(ExitStatus.Success); + } + + if (projects.length === 0) { + printVersion(sys); + printHelp(sys, buildOpts, "--build "); + return sys.exit(ExitStatus.Success); + } + + if (!sys.getModifiedTime || !sys.setModifiedTime || (buildOptions.clean && !sys.deleteFile)) { + reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--build")); + return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + } + + if (buildOptions.watch) { + if (reportWatchModeWithoutSysSupport(sys, reportDiagnostic)) return; + const buildHost = createSolutionBuilderWithWatchHost( + sys, + /*createProgram*/ undefined, + reportDiagnostic, + createBuilderStatusReporter(sys, shouldBePretty(sys, buildOptions)), + createWatchStatusReporter(sys, buildOptions) + ); + if (cb && cb.onSolutionBuilderHostCreate) cb.onSolutionBuilderHostCreate(buildHost); + updateCreateProgram(sys, buildHost); + buildHost.afterProgramEmitAndDiagnostics = program => reportStatistics(sys, program.getProgram()); + const builder = createSolutionBuilderWithWatch(buildHost, projects, buildOptions); + builder.build(); + return; + } + + const buildHost = createSolutionBuilderHost( + sys, + /*createProgram*/ undefined, + reportDiagnostic, + createBuilderStatusReporter(sys, shouldBePretty(sys, buildOptions)), + createReportErrorSummary(sys, buildOptions) + ); + if (cb && cb.onSolutionBuilderHostCreate) cb.onSolutionBuilderHostCreate(buildHost); + updateCreateProgram(sys, buildHost); + buildHost.afterProgramEmitAndDiagnostics = program => reportStatistics(sys, program.getProgram()); + const builder = createSolutionBuilder(buildHost, projects, buildOptions); + const exitStatus = buildOptions.clean ? builder.clean() : builder.build(); + if (cb && cb.onSolutionBuildComplete) cb.onSolutionBuildComplete(builder.getAllParsedConfigs()); + return sys.exit(exitStatus); + } + + function performBuild( + sys: System, + cb: ExecuteCommandLineCallbacks | undefined, + args: readonly string[] + ) { + const { buildOptions, projects, errors } = parseBuildCommand(args); + if (buildOptions.generateCpuProfile && sys.enableCPUProfiler) { + sys.enableCPUProfiler(buildOptions.generateCpuProfile, () => performBuildWorker( + sys, + cb, + buildOptions, + projects, + errors + )); + } + else { + performBuildWorker( + sys, + cb, + buildOptions, + projects, + errors + ); + } + } + + function createReportErrorSummary(sys: System, options: CompilerOptions | BuildOptions): ReportEmitErrorSummary | undefined { + return shouldBePretty(sys, options) ? + errorCount => sys.write(getErrorSummaryText(errorCount, sys.newLine)) : + undefined; + } + + function performCompilation( + sys: System, + reportDiagnostic: DiagnosticReporter, + cb: ExecuteCommandLineCallbacks | undefined, + config: ParsedCommandLine + ) { + const { fileNames, options, projectReferences } = config; + const host = createCompilerHostWorker(options, /*setParentPos*/ undefined, sys); + if (cb && cb.onCompilerHostCreate) cb.onCompilerHostCreate(host); + const currentDirectory = host.getCurrentDirectory(); + const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames()); + changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, currentDirectory, getCanonicalFileName)); + enableStatistics(sys, options); + + const programOptions: CreateProgramOptions = { + rootNames: fileNames, + options, + projectReferences, + host, + configFileParsingDiagnostics: getConfigFileParsingDiagnostics(config) + }; + const program = createProgram(programOptions); + const exitStatus = emitFilesAndReportErrorsAndGetExitStatus( + program, + reportDiagnostic, + s => sys.write(s + sys.newLine), + createReportErrorSummary(sys, options) + ); + reportStatistics(sys, program); + if (cb && cb.onCompilationComplete) cb.onCompilationComplete(config); + return sys.exit(exitStatus); + } + + function performIncrementalCompilation( + sys: System, + reportDiagnostic: DiagnosticReporter, + cb: ExecuteCommandLineCallbacks | undefined, + config: ParsedCommandLine + ) { + const { options, fileNames, projectReferences } = config; + enableStatistics(sys, options); + const host = createIncrementalCompilerHost(options, sys); + if (cb && cb.onCompilerHostCreate) cb.onCompilerHostCreate(host); + const exitStatus = ts.performIncrementalCompilation({ + host, + system: sys, + rootNames: fileNames, + options, + configFileParsingDiagnostics: getConfigFileParsingDiagnostics(config), + projectReferences, + reportDiagnostic, + reportErrorSummary: createReportErrorSummary(sys, options), + afterProgramEmitAndDiagnostics: builderProgram => reportStatistics(sys, builderProgram.getProgram()) + }); + if (cb && cb.onCompilationComplete) cb.onCompilationComplete(config); + return sys.exit(exitStatus); + } + + function updateCreateProgram(sys: System, host: { createProgram: CreateProgram; }) { + const compileUsingBuilder = host.createProgram; + host.createProgram = (rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences) => { + Debug.assert(rootNames !== undefined || (options === undefined && !!oldProgram)); + if (options !== undefined) { + enableStatistics(sys, options); + } + return compileUsingBuilder(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences); + }; + } + + function updateWatchCompilationHost(sys: System, watchCompilerHost: WatchCompilerHost) { + updateCreateProgram(sys, watchCompilerHost); + const emitFilesUsingBuilder = watchCompilerHost.afterProgramCreate!; // TODO: GH#18217 + watchCompilerHost.afterProgramCreate = builderProgram => { + emitFilesUsingBuilder(builderProgram); + reportStatistics(sys, builderProgram.getProgram()); + }; + } + + function createWatchStatusReporter(sys: System, options: CompilerOptions | BuildOptions) { + return ts.createWatchStatusReporter(sys, shouldBePretty(sys, options)); + } + + function createWatchOfConfigFile( + sys: System, + reportDiagnostic: DiagnosticReporter, + configParseResult: ParsedCommandLine, + optionsToExtend: CompilerOptions + ) { + const watchCompilerHost = createWatchCompilerHostOfConfigFile( + configParseResult.options.configFilePath!, + optionsToExtend, + sys, + /*createProgram*/ undefined, + reportDiagnostic, + createWatchStatusReporter(sys, configParseResult.options) + ); // TODO: GH#18217 + updateWatchCompilationHost(sys, watchCompilerHost); + watchCompilerHost.configFileParsingResult = configParseResult; + createWatchProgram(watchCompilerHost); + } + + function createWatchOfFilesAndCompilerOptions( + sys: System, + reportDiagnostic: DiagnosticReporter, + rootFiles: string[], + options: CompilerOptions + ) { + const watchCompilerHost = createWatchCompilerHostOfFilesAndCompilerOptions( + rootFiles, + options, + sys, + /*createProgram*/ undefined, + reportDiagnostic, + createWatchStatusReporter(sys, options) + ); + updateWatchCompilationHost(sys, watchCompilerHost); + createWatchProgram(watchCompilerHost); + } + + function canReportDiagnostics(sys: System, compilerOptions: CompilerOptions) { + return sys === ts.sys && (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics); + } + + function enableStatistics(sys: System, compilerOptions: CompilerOptions) { + if (canReportDiagnostics(sys, compilerOptions)) { + performance.enable(); + } + } + + function reportStatistics(sys: System, program: Program) { + let statistics: Statistic[]; + const compilerOptions = program.getCompilerOptions(); + if (canReportDiagnostics(sys, compilerOptions)) { + statistics = []; + const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1; + reportCountStatistic("Files", program.getSourceFiles().length); + reportCountStatistic("Lines", countLines(program)); + reportCountStatistic("Nodes", program.getNodeCount()); + reportCountStatistic("Identifiers", program.getIdentifierCount()); + reportCountStatistic("Symbols", program.getSymbolCount()); + reportCountStatistic("Types", program.getTypeCount()); + + if (memoryUsed >= 0) { + reportStatisticalValue("Memory used", Math.round(memoryUsed / 1000) + "K"); + } + + const programTime = performance.getDuration("Program"); + const bindTime = performance.getDuration("Bind"); + const checkTime = performance.getDuration("Check"); + const emitTime = performance.getDuration("Emit"); + if (compilerOptions.extendedDiagnostics) { + const caches = program.getRelationCacheSizes(); + reportCountStatistic("Assignability cache size", caches.assignable); + reportCountStatistic("Identity cache size", caches.identity); + reportCountStatistic("Subtype cache size", caches.subtype); + performance.forEachMeasure((name, duration) => reportTimeStatistic(`${name} time`, duration)); + } + else { + // Individual component times. + // Note: To match the behavior of previous versions of the compiler, the reported parse time includes + // I/O read time and processing time for triple-slash references and module imports, and the reported + // emit time includes I/O write time. We preserve this behavior so we can accurately compare times. + reportTimeStatistic("I/O read", performance.getDuration("I/O Read")); + reportTimeStatistic("I/O write", performance.getDuration("I/O Write")); + reportTimeStatistic("Parse time", programTime); + reportTimeStatistic("Bind time", bindTime); + reportTimeStatistic("Check time", checkTime); + reportTimeStatistic("Emit time", emitTime); + } + reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime); + reportStatistics(); + + performance.disable(); + } + + function reportStatistics() { + let nameSize = 0; + let valueSize = 0; + for (const { name, value } of statistics) { + if (name.length > nameSize) { + nameSize = name.length; + } + + if (value.length > valueSize) { + valueSize = value.length; + } + } + + for (const { name, value } of statistics) { + sys.write(padRight(name + ":", nameSize + 2) + padLeft(value.toString(), valueSize) + sys.newLine); + } + } + + function reportStatisticalValue(name: string, value: string) { + statistics.push({ name, value }); + } + + function reportCountStatistic(name: string, count: number) { + reportStatisticalValue(name, "" + count); + } + + function reportTimeStatistic(name: string, time: number) { + reportStatisticalValue(name, (time / 1000).toFixed(2) + "s"); + } + } + + function writeConfigFile( + sys: System, + reportDiagnostic: DiagnosticReporter, + options: CompilerOptions, + fileNames: string[] + ) { + const currentDirectory = sys.getCurrentDirectory(); + const file = normalizePath(combinePaths(currentDirectory, "tsconfig.json")); + if (sys.fileExists(file)) { + reportDiagnostic(createCompilerDiagnostic(Diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file)); + } + else { + sys.writeFile(file, generateTSConfig(options, fileNames, sys.newLine)); + reportDiagnostic(createCompilerDiagnostic(Diagnostics.Successfully_created_a_tsconfig_json_file)); + } + + return; + } +} + +if (ts.Debug.isDebugging) { + ts.Debug.enableDebugInfo(); +} + +if (ts.sys.tryEnableSourceMapsForHost && /^development$/i.test(ts.sys.getEnvironmentVariable("NODE_ENV"))) { + ts.sys.tryEnableSourceMapsForHost(); +} + +if (ts.sys.setBlocking) { + ts.sys.setBlocking(); +} diff --git a/src/tsc/tsc.ts b/src/tsc/tsc.ts index 61fff1f1005..17c45fbf0d0 100644 --- a/src/tsc/tsc.ts +++ b/src/tsc/tsc.ts @@ -1,450 +1,11 @@ -namespace ts { - interface Statistic { - name: string; - value: string; - } - - function countLines(program: Program): number { - let count = 0; - forEach(program.getSourceFiles(), file => { - count += getLineStarts(file).length; - }); - return count; - } - - let reportDiagnostic = createDiagnosticReporter(sys); - function updateReportDiagnostic(options: CompilerOptions | BuildOptions) { - if (shouldBePretty(options)) { - reportDiagnostic = createDiagnosticReporter(sys, /*pretty*/ true); - } - } - - function defaultIsPretty() { - return !!sys.writeOutputIsTTY && sys.writeOutputIsTTY(); - } - - function shouldBePretty(options: CompilerOptions | BuildOptions) { - if (!options || typeof options.pretty === "undefined") { - return defaultIsPretty(); - } - return options.pretty; - } - - function padLeft(s: string, length: number) { - while (s.length < length) { - s = " " + s; - } - return s; - } - - function padRight(s: string, length: number) { - while (s.length < length) { - s = s + " "; - } - - return s; - } - - function getOptionsForHelp(commandLine: ParsedCommandLine) { - // Sort our options by their names, (e.g. "--noImplicitAny" comes before "--watch") - return !!commandLine.options.all ? - sort(optionDeclarations, (a, b) => compareStringsCaseInsensitive(a.name, b.name)) : - filter(optionDeclarations.slice(), v => !!v.showInSimplifiedHelpView); - } - - function executeCommandLineWorker(commandLine: ParsedCommandLine) { - if (commandLine.options.build) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_build_must_be_the_first_command_line_argument)); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - - // Configuration file name (if any) - let configFileName: string | undefined; - if (commandLine.options.locale) { - validateLocaleAndSetLanguage(commandLine.options.locale, sys, commandLine.errors); - } - - // If there are any errors due to command line parsing and/or - // setting up localization, report them and quit. - if (commandLine.errors.length > 0) { - commandLine.errors.forEach(reportDiagnostic); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - - if (commandLine.options.init) { - writeConfigFile(commandLine.options, commandLine.fileNames); - return sys.exit(ExitStatus.Success); - } - - if (commandLine.options.version) { - printVersion(); - return sys.exit(ExitStatus.Success); - } - - if (commandLine.options.help || commandLine.options.all) { - printVersion(); - printHelp(getOptionsForHelp(commandLine)); - return sys.exit(ExitStatus.Success); - } - - if (commandLine.options.project) { - if (commandLine.fileNames.length !== 0) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_project_cannot_be_mixed_with_source_files_on_a_command_line)); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - - const fileOrDirectory = normalizePath(commandLine.options.project); - if (!fileOrDirectory /* current directory "." */ || sys.directoryExists(fileOrDirectory)) { - configFileName = combinePaths(fileOrDirectory, "tsconfig.json"); - if (!sys.fileExists(configFileName)) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.Cannot_find_a_tsconfig_json_file_at_the_specified_directory_Colon_0, commandLine.options.project)); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - } - else { - configFileName = fileOrDirectory; - if (!sys.fileExists(configFileName)) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_specified_path_does_not_exist_Colon_0, commandLine.options.project)); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - } - } - else if (commandLine.fileNames.length === 0) { - const searchPath = normalizePath(sys.getCurrentDirectory()); - configFileName = findConfigFile(searchPath, sys.fileExists); - } - - if (commandLine.fileNames.length === 0 && !configFileName) { - printVersion(); - printHelp(getOptionsForHelp(commandLine)); - return sys.exit(ExitStatus.Success); - } - - const currentDirectory = sys.getCurrentDirectory(); - const getCanonicalFileName = createGetCanonicalFileName(sys.useCaseSensitiveFileNames); - const commandLineOptions = convertToOptionsWithAbsolutePaths( - commandLine.options, - fileName => toPath(fileName, currentDirectory, getCanonicalFileName) - ); - if (configFileName) { - const configParseResult = parseConfigFileWithSystem(configFileName, commandLineOptions, sys, reportDiagnostic)!; // TODO: GH#18217 - if (commandLineOptions.showConfig) { - if (configParseResult.errors.length !== 0) { - updateReportDiagnostic(configParseResult.options); - configParseResult.errors.forEach(reportDiagnostic); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - // eslint-disable-next-line no-null/no-null - sys.write(JSON.stringify(convertToTSConfig(configParseResult, configFileName, sys), null, 4) + sys.newLine); - return sys.exit(ExitStatus.Success); - } - updateReportDiagnostic(configParseResult.options); - if (isWatchSet(configParseResult.options)) { - reportWatchModeWithoutSysSupport(); - createWatchOfConfigFile(configParseResult, commandLineOptions); - } - else if (isIncrementalCompilation(configParseResult.options)) { - performIncrementalCompilation(configParseResult); - } - else { - performCompilation(configParseResult.fileNames, configParseResult.projectReferences, configParseResult.options, getConfigFileParsingDiagnostics(configParseResult)); - } - } - else { - if (commandLineOptions.showConfig) { - // eslint-disable-next-line no-null/no-null - sys.write(JSON.stringify(convertToTSConfig(commandLine, combinePaths(currentDirectory, "tsconfig.json"), sys), null, 4) + sys.newLine); - return sys.exit(ExitStatus.Success); - } - updateReportDiagnostic(commandLineOptions); - if (isWatchSet(commandLineOptions)) { - reportWatchModeWithoutSysSupport(); - createWatchOfFilesAndCompilerOptions(commandLine.fileNames, commandLineOptions); - } - else if (isIncrementalCompilation(commandLineOptions)) { - performIncrementalCompilation({ - ...commandLine, - options: commandLineOptions - }); - } - else { - performCompilation(commandLine.fileNames, /*references*/ undefined, commandLineOptions); - } - } - } - - export function executeCommandLine(args: string[]): void { - if (args.length > 0 && args[0].charCodeAt(0) === CharacterCodes.minus) { - const firstOption = args[0].slice(args[0].charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase(); - if (firstOption === "build" || firstOption === "b") { - return performBuild(args.slice(1)); - } - } - - const commandLine = parseCommandLine(args); - - if (commandLine.options.generateCpuProfile && sys.enableCPUProfiler) { - sys.enableCPUProfiler(commandLine.options.generateCpuProfile, () => executeCommandLineWorker(commandLine)); - } - else { - executeCommandLineWorker(commandLine); - } - } - - function reportWatchModeWithoutSysSupport() { - if (!sys.watchFile || !sys.watchDirectory) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--watch")); - sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - } - - function performBuildWorker(buildOptions: BuildOptions, projects: string[], errors: Diagnostic[]) { - // Update to pretty if host supports it - updateReportDiagnostic(buildOptions); - - if (buildOptions.locale) { - validateLocaleAndSetLanguage(buildOptions.locale, sys, errors); - } - - if (errors.length > 0) { - errors.forEach(reportDiagnostic); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - - if (buildOptions.help) { - printVersion(); - printHelp(buildOpts, "--build "); - return sys.exit(ExitStatus.Success); - } - - if (projects.length === 0) { - printVersion(); - printHelp(buildOpts, "--build "); - return sys.exit(ExitStatus.Success); - } - - if (!sys.getModifiedTime || !sys.setModifiedTime || (buildOptions.clean && !sys.deleteFile)) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--build")); - return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - - if (buildOptions.watch) { - reportWatchModeWithoutSysSupport(); - const buildHost = createSolutionBuilderWithWatchHost(sys, /*createProgram*/ undefined, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty(buildOptions)), createWatchStatusReporter(buildOptions)); - updateCreateProgram(buildHost); - buildHost.afterProgramEmitAndDiagnostics = program => reportStatistics(program.getProgram()); - const builder = createSolutionBuilderWithWatch(buildHost, projects, buildOptions); - builder.build(); - return; - } - - const buildHost = createSolutionBuilderHost(sys, /*createProgram*/ undefined, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty(buildOptions)), createReportErrorSummary(buildOptions)); - updateCreateProgram(buildHost); - buildHost.afterProgramEmitAndDiagnostics = program => reportStatistics(program.getProgram()); - const builder = createSolutionBuilder(buildHost, projects, buildOptions); - return sys.exit(buildOptions.clean ? builder.clean() : builder.build()); - } - - function performBuild(args: string[]) { - const { buildOptions, projects, errors } = parseBuildCommand(args); - if (buildOptions.generateCpuProfile && sys.enableCPUProfiler) { - sys.enableCPUProfiler(buildOptions.generateCpuProfile, () => performBuildWorker(buildOptions, projects, errors)); - } - else { - performBuildWorker(buildOptions, projects, errors); - } - } - - function createReportErrorSummary(options: CompilerOptions | BuildOptions): ReportEmitErrorSummary | undefined { - return shouldBePretty(options) ? - errorCount => sys.write(getErrorSummaryText(errorCount, sys.newLine)) : - undefined; - } - - function performCompilation(rootNames: string[], projectReferences: readonly ProjectReference[] | undefined, options: CompilerOptions, configFileParsingDiagnostics?: readonly Diagnostic[]) { - const host = createCompilerHost(options); - const currentDirectory = host.getCurrentDirectory(); - const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames()); - changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, currentDirectory, getCanonicalFileName)); - enableStatistics(options); - - const programOptions: CreateProgramOptions = { - rootNames, - options, - projectReferences, - host, - configFileParsingDiagnostics - }; - const program = createProgram(programOptions); - const exitStatus = emitFilesAndReportErrorsAndGetExitStatus( - program, - reportDiagnostic, - s => sys.write(s + sys.newLine), - createReportErrorSummary(options) - ); - reportStatistics(program); - return sys.exit(exitStatus); - } - - function performIncrementalCompilation(config: ParsedCommandLine) { - const { options, fileNames, projectReferences } = config; - enableStatistics(options); - return sys.exit(ts.performIncrementalCompilation({ - rootNames: fileNames, - options, - configFileParsingDiagnostics: getConfigFileParsingDiagnostics(config), - projectReferences, - reportDiagnostic, - reportErrorSummary: createReportErrorSummary(options), - afterProgramEmitAndDiagnostics: builderProgram => reportStatistics(builderProgram.getProgram()) - })); - } - - function updateCreateProgram(host: { createProgram: CreateProgram; }) { - const compileUsingBuilder = host.createProgram; - host.createProgram = (rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences) => { - Debug.assert(rootNames !== undefined || (options === undefined && !!oldProgram)); - if (options !== undefined) { - enableStatistics(options); - } - return compileUsingBuilder(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences); - }; - } - - function updateWatchCompilationHost(watchCompilerHost: WatchCompilerHost) { - updateCreateProgram(watchCompilerHost); - const emitFilesUsingBuilder = watchCompilerHost.afterProgramCreate!; // TODO: GH#18217 - watchCompilerHost.afterProgramCreate = builderProgram => { - emitFilesUsingBuilder(builderProgram); - reportStatistics(builderProgram.getProgram()); - }; - } - - function createWatchStatusReporter(options: CompilerOptions | BuildOptions) { - return ts.createWatchStatusReporter(sys, shouldBePretty(options)); - } - - function createWatchOfConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions) { - const watchCompilerHost = createWatchCompilerHostOfConfigFile(configParseResult.options.configFilePath!, optionsToExtend, sys, /*createProgram*/ undefined, reportDiagnostic, createWatchStatusReporter(configParseResult.options)); // TODO: GH#18217 - updateWatchCompilationHost(watchCompilerHost); - watchCompilerHost.configFileParsingResult = configParseResult; - createWatchProgram(watchCompilerHost); - } - - function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions) { - const watchCompilerHost = createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, sys, /*createProgram*/ undefined, reportDiagnostic, createWatchStatusReporter(options)); - updateWatchCompilationHost(watchCompilerHost); - createWatchProgram(watchCompilerHost); - } - - function enableStatistics(compilerOptions: CompilerOptions) { - if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) { - performance.enable(); - } - } - - function reportStatistics(program: Program) { - let statistics: Statistic[]; - const compilerOptions = program.getCompilerOptions(); - if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) { - statistics = []; - const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1; - reportCountStatistic("Files", program.getSourceFiles().length); - reportCountStatistic("Lines", countLines(program)); - reportCountStatistic("Nodes", program.getNodeCount()); - reportCountStatistic("Identifiers", program.getIdentifierCount()); - reportCountStatistic("Symbols", program.getSymbolCount()); - reportCountStatistic("Types", program.getTypeCount()); - - if (memoryUsed >= 0) { - reportStatisticalValue("Memory used", Math.round(memoryUsed / 1000) + "K"); - } - - const programTime = performance.getDuration("Program"); - const bindTime = performance.getDuration("Bind"); - const checkTime = performance.getDuration("Check"); - const emitTime = performance.getDuration("Emit"); - if (compilerOptions.extendedDiagnostics) { - const caches = program.getRelationCacheSizes(); - reportCountStatistic("Assignability cache size", caches.assignable); - reportCountStatistic("Identity cache size", caches.identity); - reportCountStatistic("Subtype cache size", caches.subtype); - performance.forEachMeasure((name, duration) => reportTimeStatistic(`${name} time`, duration)); - } - else { - // Individual component times. - // Note: To match the behavior of previous versions of the compiler, the reported parse time includes - // I/O read time and processing time for triple-slash references and module imports, and the reported - // emit time includes I/O write time. We preserve this behavior so we can accurately compare times. - reportTimeStatistic("I/O read", performance.getDuration("I/O Read")); - reportTimeStatistic("I/O write", performance.getDuration("I/O Write")); - reportTimeStatistic("Parse time", programTime); - reportTimeStatistic("Bind time", bindTime); - reportTimeStatistic("Check time", checkTime); - reportTimeStatistic("Emit time", emitTime); - } - reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime); - reportStatistics(); - - performance.disable(); - } - - function reportStatistics() { - let nameSize = 0; - let valueSize = 0; - for (const { name, value } of statistics) { - if (name.length > nameSize) { - nameSize = name.length; - } - - if (value.length > valueSize) { - valueSize = value.length; - } - } - - for (const { name, value } of statistics) { - sys.write(padRight(name + ":", nameSize + 2) + padLeft(value.toString(), valueSize) + sys.newLine); - } - } - - function reportStatisticalValue(name: string, value: string) { - statistics.push({ name, value }); - } - - function reportCountStatistic(name: string, count: number) { - reportStatisticalValue(name, "" + count); - } - - function reportTimeStatistic(name: string, time: number) { - reportStatisticalValue(name, (time / 1000).toFixed(2) + "s"); - } - } - - function writeConfigFile(options: CompilerOptions, fileNames: string[]) { - const currentDirectory = sys.getCurrentDirectory(); - const file = normalizePath(combinePaths(currentDirectory, "tsconfig.json")); - if (sys.fileExists(file)) { - reportDiagnostic(createCompilerDiagnostic(Diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file)); - } - else { - sys.writeFile(file, generateTSConfig(options, fileNames, sys.newLine)); - reportDiagnostic(createCompilerDiagnostic(Diagnostics.Successfully_created_a_tsconfig_json_file)); - } - - return; - } -} - -if (ts.Debug.isDebugging) { - ts.Debug.enableDebugInfo(); -} - -if (ts.sys.tryEnableSourceMapsForHost && /^development$/i.test(ts.sys.getEnvironmentVariable("NODE_ENV"))) { - ts.sys.tryEnableSourceMapsForHost(); -} - -if (ts.sys.setBlocking) { - ts.sys.setBlocking(); -} - -ts.executeCommandLine(ts.sys.args); +// This file actually uses arguments passed on commandline and executes it +ts.executeCommandLine( + ts.sys, + { + onCompilerHostCreate: ts.noop, + onCompilationComplete: ts.noop, + onSolutionBuilderHostCreate: ts.noop, + onSolutionBuildComplete: ts.noop + }, + ts.sys.args +); diff --git a/src/tsc/tsconfig.json b/src/tsc/tsconfig.json index e97cedc5de2..7de3cb82a95 100644 --- a/src/tsc/tsconfig.json +++ b/src/tsc/tsconfig.json @@ -4,6 +4,7 @@ "outFile": "../../built/local/tsc.js" }, "files": [ + "executeCommandLine.ts", "tsc.ts" ], "references": [