• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts {
2    interface Statistic {
3        name: string;
4        value: string;
5    }
6
7    function countLines(program: Program): Map<number> {
8        const counts = getCountsMap();
9        forEach(program.getSourceFiles(), file => {
10            const key = getCountKey(program, file);
11            const lineCount = getLineStarts(file).length;
12            counts.set(key, counts.get(key)! + lineCount);
13        });
14        return counts;
15    }
16
17    function countNodes(program: Program): Map<number> {
18        const counts = getCountsMap();
19        forEach(program.getSourceFiles(), file => {
20            const key = getCountKey(program, file);
21            counts.set(key, counts.get(key)! + file.nodeCount);
22        });
23        return counts;
24    }
25
26    function getCountsMap() {
27        const counts = createMap<number>();
28        counts.set("Library", 0);
29        counts.set("Definitions", 0);
30        counts.set("TypeScript", 0);
31        counts.set("JavaScript", 0);
32        counts.set("JSON", 0);
33        counts.set("Other", 0);
34        return counts;
35    }
36
37    function getCountKey(program: Program, file: SourceFile) {
38        if (program.isSourceFileDefaultLibrary(file)) {
39            return "Library";
40        }
41        else if (file.isDeclarationFile) {
42            return "Definitions";
43        }
44
45        const path = file.path;
46        if (fileExtensionIsOneOf(path, supportedTSExtensions)) {
47            return "TypeScript";
48        }
49        else if (fileExtensionIsOneOf(path, supportedJSExtensions)) {
50            return "JavaScript";
51        }
52        else if (fileExtensionIs(path, Extension.Json)) {
53            return "JSON";
54        }
55        else {
56            return "Other";
57        }
58    }
59
60    function updateReportDiagnostic(
61        sys: System,
62        existing: DiagnosticReporter,
63        options: CompilerOptions | BuildOptions
64    ): DiagnosticReporter {
65        return shouldBePretty(sys, options) ?
66            createDiagnosticReporter(sys, /*pretty*/ true) :
67            existing;
68    }
69
70    function defaultIsPretty(sys: System) {
71        return !!sys.writeOutputIsTTY && sys.writeOutputIsTTY();
72    }
73
74    function shouldBePretty(sys: System, options: CompilerOptions | BuildOptions) {
75        if (!options || typeof options.pretty === "undefined") {
76            return defaultIsPretty(sys);
77        }
78        return options.pretty;
79    }
80
81    function getOptionsForHelp(commandLine: ParsedCommandLine) {
82        // Sort our options by their names, (e.g. "--noImplicitAny" comes before "--watch")
83        return !!commandLine.options.all ?
84            sort(optionDeclarations, (a, b) => compareStringsCaseInsensitive(a.name, b.name)) :
85            filter(optionDeclarations.slice(), v => !!v.showInSimplifiedHelpView);
86    }
87
88    function printVersion(sys: System) {
89        sys.write(getDiagnosticText(Diagnostics.Version_0, version) + sys.newLine);
90    }
91
92    function printHelp(sys: System, optionsList: readonly CommandLineOption[], syntaxPrefix = "") {
93        const output: string[] = [];
94
95        // We want to align our "syntax" and "examples" commands to a certain margin.
96        const syntaxLength = getDiagnosticText(Diagnostics.Syntax_Colon_0, "").length;
97        const examplesLength = getDiagnosticText(Diagnostics.Examples_Colon_0, "").length;
98        let marginLength = Math.max(syntaxLength, examplesLength);
99
100        // Build up the syntactic skeleton.
101        let syntax = makePadding(marginLength - syntaxLength);
102        syntax += `tsc ${syntaxPrefix}[${getDiagnosticText(Diagnostics.options)}] [${getDiagnosticText(Diagnostics.file)}...]`;
103
104        output.push(getDiagnosticText(Diagnostics.Syntax_Colon_0, syntax));
105        output.push(sys.newLine + sys.newLine);
106
107        // Build up the list of examples.
108        const padding = makePadding(marginLength);
109        output.push(getDiagnosticText(Diagnostics.Examples_Colon_0, makePadding(marginLength - examplesLength) + "tsc hello.ts") + sys.newLine);
110        output.push(padding + "tsc --outFile file.js file.ts" + sys.newLine);
111        output.push(padding + "tsc @args.txt" + sys.newLine);
112        output.push(padding + "tsc --build tsconfig.json" + sys.newLine);
113        output.push(sys.newLine);
114
115        output.push(getDiagnosticText(Diagnostics.Options_Colon) + sys.newLine);
116
117        // We want our descriptions to align at the same column in our output,
118        // so we keep track of the longest option usage string.
119        marginLength = 0;
120        const usageColumn: string[] = []; // Things like "-d, --declaration" go in here.
121        const descriptionColumn: string[] = [];
122
123        const optionsDescriptionMap = new Map<string, string[]>();  // Map between option.description and list of option.type if it is a kind
124
125        for (const option of optionsList) {
126            // If an option lacks a description,
127            // it is not officially supported.
128            if (!option.description) {
129                continue;
130            }
131
132            let usageText = " ";
133            if (option.shortName) {
134                usageText += "-" + option.shortName;
135                usageText += getParamType(option);
136                usageText += ", ";
137            }
138
139            usageText += "--" + option.name;
140            usageText += getParamType(option);
141
142            usageColumn.push(usageText);
143            let description: string;
144
145            if (option.name === "lib") {
146                description = getDiagnosticText(option.description);
147                const element = (<CommandLineOptionOfListType>option).element;
148                const typeMap = <ESMap<string, number | string>>element.type;
149                optionsDescriptionMap.set(description, arrayFrom(typeMap.keys()).map(key => `'${key}'`));
150            }
151            else {
152                description = getDiagnosticText(option.description);
153            }
154
155            descriptionColumn.push(description);
156
157            // Set the new margin for the description column if necessary.
158            marginLength = Math.max(usageText.length, marginLength);
159        }
160
161        // Special case that can't fit in the loop.
162        const usageText = " @<" + getDiagnosticText(Diagnostics.file) + ">";
163        usageColumn.push(usageText);
164        descriptionColumn.push(getDiagnosticText(Diagnostics.Insert_command_line_options_and_files_from_a_file));
165        marginLength = Math.max(usageText.length, marginLength);
166
167        // Print out each row, aligning all the descriptions on the same column.
168        for (let i = 0; i < usageColumn.length; i++) {
169            const usage = usageColumn[i];
170            const description = descriptionColumn[i];
171            const kindsList = optionsDescriptionMap.get(description);
172            output.push(usage + makePadding(marginLength - usage.length + 2) + description + sys.newLine);
173
174            if (kindsList) {
175                output.push(makePadding(marginLength + 4));
176                for (const kind of kindsList) {
177                    output.push(kind + " ");
178                }
179                output.push(sys.newLine);
180            }
181        }
182
183        for (const line of output) {
184            sys.write(line);
185        }
186        return;
187
188        function getParamType(option: CommandLineOption) {
189            if (option.paramType !== undefined) {
190                return " " + getDiagnosticText(option.paramType);
191            }
192            return "";
193        }
194
195        function makePadding(paddingLength: number): string {
196            return Array(paddingLength + 1).join(" ");
197        }
198    }
199
200    function executeCommandLineWorker(
201        sys: System,
202        cb: ExecuteCommandLineCallbacks,
203        commandLine: ParsedCommandLine,
204    ) {
205        let reportDiagnostic = createDiagnosticReporter(sys);
206        if (commandLine.options.build) {
207            reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_build_must_be_the_first_command_line_argument));
208            return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
209        }
210
211        // Configuration file name (if any)
212        let configFileName: string | undefined;
213        if (commandLine.options.locale) {
214            validateLocaleAndSetLanguage(commandLine.options.locale, sys, commandLine.errors);
215        }
216
217        // If there are any errors due to command line parsing and/or
218        // setting up localization, report them and quit.
219        if (commandLine.errors.length > 0) {
220            commandLine.errors.forEach(reportDiagnostic);
221            return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
222        }
223
224        if (commandLine.options.init) {
225            writeConfigFile(sys, reportDiagnostic, commandLine.options, commandLine.fileNames);
226            return sys.exit(ExitStatus.Success);
227        }
228
229        if (commandLine.options.version) {
230            printVersion(sys);
231            return sys.exit(ExitStatus.Success);
232        }
233
234        if (commandLine.options.help || commandLine.options.all) {
235            printVersion(sys);
236            printHelp(sys, getOptionsForHelp(commandLine));
237            return sys.exit(ExitStatus.Success);
238        }
239
240        if (commandLine.options.watch && commandLine.options.listFilesOnly) {
241            reportDiagnostic(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "watch", "listFilesOnly"));
242            return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
243        }
244
245        if (commandLine.options.project) {
246            if (commandLine.fileNames.length !== 0) {
247                reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_project_cannot_be_mixed_with_source_files_on_a_command_line));
248                return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
249            }
250
251            const fileOrDirectory = normalizePath(commandLine.options.project);
252            if (!fileOrDirectory /* current directory "." */ || sys.directoryExists(fileOrDirectory)) {
253                configFileName = combinePaths(fileOrDirectory, "tsconfig.json");
254                if (!sys.fileExists(configFileName)) {
255                    reportDiagnostic(createCompilerDiagnostic(Diagnostics.Cannot_find_a_tsconfig_json_file_at_the_specified_directory_Colon_0, commandLine.options.project));
256                    return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
257                }
258            }
259            else {
260                configFileName = fileOrDirectory;
261                if (!sys.fileExists(configFileName)) {
262                    reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_specified_path_does_not_exist_Colon_0, commandLine.options.project));
263                    return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
264                }
265            }
266        }
267        else if (commandLine.fileNames.length === 0) {
268            const searchPath = normalizePath(sys.getCurrentDirectory());
269            configFileName = findConfigFile(searchPath, fileName => sys.fileExists(fileName));
270        }
271
272        if (commandLine.fileNames.length === 0 && !configFileName) {
273            if (commandLine.options.showConfig) {
274                reportDiagnostic(createCompilerDiagnostic(Diagnostics.Cannot_find_a_tsconfig_json_file_at_the_current_directory_Colon_0, normalizePath(sys.getCurrentDirectory())));
275            }
276            else {
277                printVersion(sys);
278                printHelp(sys, getOptionsForHelp(commandLine));
279            }
280            return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
281        }
282
283        const currentDirectory = sys.getCurrentDirectory();
284        const commandLineOptions = convertToOptionsWithAbsolutePaths(
285            commandLine.options,
286            fileName => getNormalizedAbsolutePath(fileName, currentDirectory)
287        );
288        if (configFileName) {
289            const configParseResult = parseConfigFileWithSystem(configFileName, commandLineOptions, commandLine.watchOptions, sys, reportDiagnostic)!; // TODO: GH#18217
290            if (commandLineOptions.showConfig) {
291                if (configParseResult.errors.length !== 0) {
292                    reportDiagnostic = updateReportDiagnostic(
293                        sys,
294                        reportDiagnostic,
295                        configParseResult.options
296                    );
297                    configParseResult.errors.forEach(reportDiagnostic);
298                    return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
299                }
300                // eslint-disable-next-line no-null/no-null
301                sys.write(JSON.stringify(convertToTSConfig(configParseResult, configFileName, sys), null, 4) + sys.newLine);
302                return sys.exit(ExitStatus.Success);
303            }
304            reportDiagnostic = updateReportDiagnostic(
305                sys,
306                reportDiagnostic,
307                configParseResult.options
308            );
309            if (isWatchSet(configParseResult.options)) {
310                if (reportWatchModeWithoutSysSupport(sys, reportDiagnostic)) return;
311                return createWatchOfConfigFile(
312                    sys,
313                    cb,
314                    reportDiagnostic,
315                    configParseResult,
316                    commandLineOptions,
317                    commandLine.watchOptions,
318                );
319            }
320            else if (isIncrementalCompilation(configParseResult.options)) {
321                performIncrementalCompilation(
322                    sys,
323                    cb,
324                    reportDiagnostic,
325                    configParseResult
326                );
327            }
328            else {
329                performCompilation(
330                    sys,
331                    cb,
332                    reportDiagnostic,
333                    configParseResult
334                );
335            }
336        }
337        else {
338            if (commandLineOptions.showConfig) {
339                // eslint-disable-next-line no-null/no-null
340                sys.write(JSON.stringify(convertToTSConfig(commandLine, combinePaths(currentDirectory, "tsconfig.json"), sys), null, 4) + sys.newLine);
341                return sys.exit(ExitStatus.Success);
342            }
343            reportDiagnostic = updateReportDiagnostic(
344                sys,
345                reportDiagnostic,
346                commandLineOptions
347            );
348            if (isWatchSet(commandLineOptions)) {
349                if (reportWatchModeWithoutSysSupport(sys, reportDiagnostic)) return;
350                return createWatchOfFilesAndCompilerOptions(
351                    sys,
352                    cb,
353                    reportDiagnostic,
354                    commandLine.fileNames,
355                    commandLineOptions,
356                    commandLine.watchOptions,
357                );
358            }
359            else if (isIncrementalCompilation(commandLineOptions)) {
360                performIncrementalCompilation(
361                    sys,
362                    cb,
363                    reportDiagnostic,
364                    { ...commandLine, options: commandLineOptions }
365                );
366            }
367            else {
368                performCompilation(
369                    sys,
370                    cb,
371                    reportDiagnostic,
372                    { ...commandLine, options: commandLineOptions }
373                );
374            }
375        }
376    }
377
378    export function isBuild(commandLineArgs: readonly string[]) {
379        if (commandLineArgs.length > 0 && commandLineArgs[0].charCodeAt(0) === CharacterCodes.minus) {
380            const firstOption = commandLineArgs[0].slice(commandLineArgs[0].charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase();
381            return firstOption === "build" || firstOption === "b";
382        }
383        return false;
384    }
385
386    export type ExecuteCommandLineCallbacks = (program: Program | EmitAndSemanticDiagnosticsBuilderProgram | ParsedCommandLine) => void;
387    export function executeCommandLine(
388        system: System,
389        cb: ExecuteCommandLineCallbacks,
390        commandLineArgs: readonly string[],
391    ) {
392        if (isBuild(commandLineArgs)) {
393            const { buildOptions, watchOptions, projects, errors } = parseBuildCommand(commandLineArgs.slice(1));
394            if (buildOptions.generateCpuProfile && system.enableCPUProfiler) {
395                system.enableCPUProfiler(buildOptions.generateCpuProfile, () => performBuild(
396                    system,
397                    cb,
398                    buildOptions,
399                    watchOptions,
400                    projects,
401                    errors
402                ));
403            }
404            else {
405                return performBuild(
406                    system,
407                    cb,
408                    buildOptions,
409                    watchOptions,
410                    projects,
411                    errors
412                );
413            }
414        }
415
416        const commandLine = parseCommandLine(commandLineArgs, path => system.readFile(path));
417        if (commandLine.options.generateCpuProfile && system.enableCPUProfiler) {
418            system.enableCPUProfiler(commandLine.options.generateCpuProfile, () => executeCommandLineWorker(
419                system,
420                cb,
421                commandLine,
422            ));
423        }
424        else {
425            return executeCommandLineWorker(system, cb, commandLine);
426        }
427    }
428
429    function reportWatchModeWithoutSysSupport(sys: System, reportDiagnostic: DiagnosticReporter) {
430        if (!sys.watchFile || !sys.watchDirectory) {
431            reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--watch"));
432            sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
433            return true;
434        }
435        return false;
436    }
437
438    function performBuild(
439        sys: System,
440        cb: ExecuteCommandLineCallbacks,
441        buildOptions: BuildOptions,
442        watchOptions: WatchOptions | undefined,
443        projects: string[],
444        errors: Diagnostic[]
445    ) {
446        // Update to pretty if host supports it
447        const reportDiagnostic = updateReportDiagnostic(
448            sys,
449            createDiagnosticReporter(sys),
450            buildOptions
451        );
452
453        if (buildOptions.locale) {
454            validateLocaleAndSetLanguage(buildOptions.locale, sys, errors);
455        }
456
457        if (errors.length > 0) {
458            errors.forEach(reportDiagnostic);
459            return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
460        }
461
462        if (buildOptions.help) {
463            printVersion(sys);
464            printHelp(sys, buildOpts, "--build ");
465            return sys.exit(ExitStatus.Success);
466        }
467
468        if (projects.length === 0) {
469            printVersion(sys);
470            printHelp(sys, buildOpts, "--build ");
471            return sys.exit(ExitStatus.Success);
472        }
473
474        if (!sys.getModifiedTime || !sys.setModifiedTime || (buildOptions.clean && !sys.deleteFile)) {
475            reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--build"));
476            return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
477        }
478
479        if (buildOptions.watch) {
480            if (reportWatchModeWithoutSysSupport(sys, reportDiagnostic)) return;
481            const buildHost = createSolutionBuilderWithWatchHost(
482                sys,
483                /*createProgram*/ undefined,
484                reportDiagnostic,
485                createBuilderStatusReporter(sys, shouldBePretty(sys, buildOptions)),
486                createWatchStatusReporter(sys, buildOptions)
487            );
488            updateSolutionBuilderHost(sys, cb, buildHost);
489            const builder = createSolutionBuilderWithWatch(buildHost, projects, buildOptions, watchOptions);
490            builder.build();
491            return builder;
492        }
493
494        const buildHost = createSolutionBuilderHost(
495            sys,
496            /*createProgram*/ undefined,
497            reportDiagnostic,
498            createBuilderStatusReporter(sys, shouldBePretty(sys, buildOptions)),
499            createReportErrorSummary(sys, buildOptions)
500        );
501        updateSolutionBuilderHost(sys, cb, buildHost);
502        const builder = createSolutionBuilder(buildHost, projects, buildOptions);
503        const exitStatus = buildOptions.clean ? builder.clean() : builder.build();
504        tracing?.dumpLegend();
505        return sys.exit(exitStatus);
506    }
507
508    function createReportErrorSummary(sys: System, options: CompilerOptions | BuildOptions): ReportEmitErrorSummary | undefined {
509        return shouldBePretty(sys, options) ?
510            errorCount => sys.write(getErrorSummaryText(errorCount, sys.newLine)) :
511            undefined;
512    }
513
514    function performCompilation(
515        sys: System,
516        cb: ExecuteCommandLineCallbacks,
517        reportDiagnostic: DiagnosticReporter,
518        config: ParsedCommandLine
519    ) {
520        const { fileNames, options, projectReferences } = config;
521        const host = createCompilerHostWorker(options, /*setParentPos*/ undefined, sys);
522        const currentDirectory = host.getCurrentDirectory();
523        const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
524        changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, currentDirectory, getCanonicalFileName));
525        enableStatisticsAndTracing(sys, options, /*isBuildMode*/ false);
526
527        const programOptions: CreateProgramOptions = {
528            rootNames: fileNames,
529            options,
530            projectReferences,
531            host,
532            configFileParsingDiagnostics: getConfigFileParsingDiagnostics(config)
533        };
534        const program = createProgram(programOptions);
535        const exitStatus = emitFilesAndReportErrorsAndGetExitStatus(
536            program,
537            reportDiagnostic,
538            s => sys.write(s + sys.newLine),
539            createReportErrorSummary(sys, options)
540        );
541        reportStatistics(sys, program);
542        cb(program);
543        return sys.exit(exitStatus);
544    }
545
546    function performIncrementalCompilation(
547        sys: System,
548        cb: ExecuteCommandLineCallbacks,
549        reportDiagnostic: DiagnosticReporter,
550        config: ParsedCommandLine
551    ) {
552        const { options, fileNames, projectReferences } = config;
553        enableStatisticsAndTracing(sys, options, /*isBuildMode*/ false);
554        const host = createIncrementalCompilerHost(options, sys);
555        const exitStatus = ts.performIncrementalCompilation({
556            host,
557            system: sys,
558            rootNames: fileNames,
559            options,
560            configFileParsingDiagnostics: getConfigFileParsingDiagnostics(config),
561            projectReferences,
562            reportDiagnostic,
563            reportErrorSummary: createReportErrorSummary(sys, options),
564            afterProgramEmitAndDiagnostics: builderProgram => {
565                reportStatistics(sys, builderProgram.getProgram());
566                cb(builderProgram);
567            }
568        });
569        return sys.exit(exitStatus);
570    }
571
572    function updateSolutionBuilderHost(
573        sys: System,
574        cb: ExecuteCommandLineCallbacks,
575        buildHost: SolutionBuilderHostBase<EmitAndSemanticDiagnosticsBuilderProgram>
576    ) {
577        updateCreateProgram(sys, buildHost);
578        buildHost.afterProgramEmitAndDiagnostics = program => {
579            reportStatistics(sys, program.getProgram());
580            cb(program);
581        };
582        buildHost.afterEmitBundle = cb;
583    }
584
585    function updateCreateProgram<T extends BuilderProgram>(sys: System, host: { createProgram: CreateProgram<T>; }) {
586        const compileUsingBuilder = host.createProgram;
587        host.createProgram = (rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences) => {
588            Debug.assert(rootNames !== undefined || (options === undefined && !!oldProgram));
589            if (options !== undefined) {
590                enableStatisticsAndTracing(sys, options, /*isBuildMode*/ true);
591            }
592            return compileUsingBuilder(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences);
593        };
594    }
595
596    function updateWatchCompilationHost(
597        sys: System,
598        cb: ExecuteCommandLineCallbacks,
599        watchCompilerHost: WatchCompilerHost<EmitAndSemanticDiagnosticsBuilderProgram>,
600    ) {
601        updateCreateProgram(sys, watchCompilerHost);
602        const emitFilesUsingBuilder = watchCompilerHost.afterProgramCreate!; // TODO: GH#18217
603        watchCompilerHost.afterProgramCreate = builderProgram => {
604            emitFilesUsingBuilder(builderProgram);
605            reportStatistics(sys, builderProgram.getProgram());
606            cb(builderProgram);
607        };
608    }
609
610    function createWatchStatusReporter(sys: System, options: CompilerOptions | BuildOptions) {
611        return ts.createWatchStatusReporter(sys, shouldBePretty(sys, options));
612    }
613
614    function createWatchOfConfigFile(
615        system: System,
616        cb: ExecuteCommandLineCallbacks,
617        reportDiagnostic: DiagnosticReporter,
618        configParseResult: ParsedCommandLine,
619        optionsToExtend: CompilerOptions,
620        watchOptionsToExtend: WatchOptions | undefined,
621    ) {
622        const watchCompilerHost = createWatchCompilerHostOfConfigFile({
623            configFileName: configParseResult.options.configFilePath!,
624            optionsToExtend,
625            watchOptionsToExtend,
626            system,
627            reportDiagnostic,
628            reportWatchStatus: createWatchStatusReporter(system, configParseResult.options)
629        });
630        updateWatchCompilationHost(system, cb, watchCompilerHost);
631        watchCompilerHost.configFileParsingResult = configParseResult;
632        return createWatchProgram(watchCompilerHost);
633    }
634
635    function createWatchOfFilesAndCompilerOptions(
636        system: System,
637        cb: ExecuteCommandLineCallbacks,
638        reportDiagnostic: DiagnosticReporter,
639        rootFiles: string[],
640        options: CompilerOptions,
641        watchOptions: WatchOptions | undefined,
642    ) {
643        const watchCompilerHost = createWatchCompilerHostOfFilesAndCompilerOptions({
644            rootFiles,
645            options,
646            watchOptions,
647            system,
648            reportDiagnostic,
649            reportWatchStatus: createWatchStatusReporter(system, options)
650        });
651        updateWatchCompilationHost(system, cb, watchCompilerHost);
652        return createWatchProgram(watchCompilerHost);
653    }
654
655    function canReportDiagnostics(system: System, compilerOptions: CompilerOptions) {
656        return system === sys && (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics);
657    }
658
659    function canTrace(system: System, compilerOptions: CompilerOptions) {
660        return system === sys && compilerOptions.generateTrace;
661    }
662
663    function enableStatisticsAndTracing(system: System, compilerOptions: CompilerOptions, isBuildMode: boolean) {
664        if (canReportDiagnostics(system, compilerOptions)) {
665            performance.enable(system);
666        }
667
668        if (canTrace(system, compilerOptions)) {
669            startTracing(isBuildMode ? tracingEnabled.Mode.Build : tracingEnabled.Mode.Project,
670                         compilerOptions.generateTrace!, compilerOptions.configFilePath);
671        }
672    }
673
674    function reportStatistics(sys: System, program: Program) {
675        const compilerOptions = program.getCompilerOptions();
676
677        if (canTrace(sys, compilerOptions)) {
678            tracing?.stopTracing(program.getTypeCatalog());
679        }
680
681        let statistics: Statistic[];
682        if (canReportDiagnostics(sys, compilerOptions)) {
683            statistics = [];
684            const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1;
685            reportCountStatistic("Files", program.getSourceFiles().length);
686
687            const lineCounts = countLines(program);
688            const nodeCounts = countNodes(program);
689            if (compilerOptions.extendedDiagnostics) {
690                for (const key of arrayFrom(lineCounts.keys())) {
691                    reportCountStatistic("Lines of " + key, lineCounts.get(key)!);
692                }
693                for (const key of arrayFrom(nodeCounts.keys())) {
694                    reportCountStatistic("Nodes of " + key, nodeCounts.get(key)!);
695                }
696            }
697            else {
698                reportCountStatistic("Lines", reduceLeftIterator(lineCounts.values(), (sum, count) => sum + count, 0));
699                reportCountStatistic("Nodes", reduceLeftIterator(nodeCounts.values(), (sum, count) => sum + count, 0));
700            }
701
702            reportCountStatistic("Identifiers", program.getIdentifierCount());
703            reportCountStatistic("Symbols", program.getSymbolCount());
704            reportCountStatistic("Types", program.getTypeCount());
705            reportCountStatistic("Instantiations", program.getInstantiationCount());
706
707            if (memoryUsed >= 0) {
708                reportStatisticalValue("Memory used", Math.round(memoryUsed / 1000) + "K");
709            }
710
711            const isPerformanceEnabled = performance.isEnabled();
712            const programTime = isPerformanceEnabled ? performance.getDuration("Program") : 0;
713            const bindTime = isPerformanceEnabled ? performance.getDuration("Bind") : 0;
714            const checkTime = isPerformanceEnabled ? performance.getDuration("Check") : 0;
715            const emitTime = isPerformanceEnabled ? performance.getDuration("Emit") : 0;
716            if (compilerOptions.extendedDiagnostics) {
717                const caches = program.getRelationCacheSizes();
718                reportCountStatistic("Assignability cache size", caches.assignable);
719                reportCountStatistic("Identity cache size", caches.identity);
720                reportCountStatistic("Subtype cache size", caches.subtype);
721                reportCountStatistic("Strict subtype cache size", caches.strictSubtype);
722                if (isPerformanceEnabled) {
723                    performance.forEachMeasure((name, duration) => reportTimeStatistic(`${name} time`, duration));
724                }
725            }
726            else if (isPerformanceEnabled) {
727                // Individual component times.
728                // Note: To match the behavior of previous versions of the compiler, the reported parse time includes
729                // I/O read time and processing time for triple-slash references and module imports, and the reported
730                // emit time includes I/O write time. We preserve this behavior so we can accurately compare times.
731                reportTimeStatistic("I/O read", performance.getDuration("I/O Read"));
732                reportTimeStatistic("I/O write", performance.getDuration("I/O Write"));
733                reportTimeStatistic("Parse time", programTime);
734                reportTimeStatistic("Bind time", bindTime);
735                reportTimeStatistic("Check time", checkTime);
736                reportTimeStatistic("Emit time", emitTime);
737            }
738            if (isPerformanceEnabled) {
739                reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime);
740            }
741            reportStatistics();
742            if (!isPerformanceEnabled) {
743                sys.write(Diagnostics.Performance_timings_for_diagnostics_or_extendedDiagnostics_are_not_available_in_this_session_A_native_implementation_of_the_Web_Performance_API_could_not_be_found.message + "\n");
744            }
745            else {
746                performance.disable();
747            }
748        }
749
750        function reportStatistics() {
751            let nameSize = 0;
752            let valueSize = 0;
753            for (const { name, value } of statistics) {
754                if (name.length > nameSize) {
755                    nameSize = name.length;
756                }
757
758                if (value.length > valueSize) {
759                    valueSize = value.length;
760                }
761            }
762
763            for (const { name, value } of statistics) {
764                sys.write(padRight(name + ":", nameSize + 2) + padLeft(value.toString(), valueSize) + sys.newLine);
765            }
766        }
767
768        function reportStatisticalValue(name: string, value: string) {
769            statistics.push({ name, value });
770        }
771
772        function reportCountStatistic(name: string, count: number) {
773            reportStatisticalValue(name, "" + count);
774        }
775
776        function reportTimeStatistic(name: string, time: number) {
777            reportStatisticalValue(name, (time / 1000).toFixed(2) + "s");
778        }
779    }
780
781    function writeConfigFile(
782        sys: System,
783        reportDiagnostic: DiagnosticReporter,
784        options: CompilerOptions,
785        fileNames: string[]
786    ) {
787        const currentDirectory = sys.getCurrentDirectory();
788        const file = normalizePath(combinePaths(currentDirectory, "tsconfig.json"));
789        if (sys.fileExists(file)) {
790            reportDiagnostic(createCompilerDiagnostic(Diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file));
791        }
792        else {
793            sys.writeFile(file, generateTSConfig(options, fileNames, sys.newLine));
794            reportDiagnostic(createCompilerDiagnostic(Diagnostics.Successfully_created_a_tsconfig_json_file));
795        }
796
797        return;
798    }
799}
800