export async function createProgramAndEmit()

in packages/concatjs/internal/tsc_wrapped/tsc_wrapped.ts [295:491]


export async function createProgramAndEmit(
    fileLoader: FileLoader, options: ts.CompilerOptions,
    bazelOpts: BazelOptions, files: string[], disabledTsetseRules: string[]):
    Promise<{program?: ts.Program, diagnostics: ts.Diagnostic[]}> {
  // Beware! createProgramAndEmit must not print to console, nor exit etc.
  // Handle errors by reporting and returning diagnostics.
  perfTrace.snapshotMemoryUsage();
  cache.resetStats();
  cache.traceStats();

  const compilerHostDelegate =
      ts.createCompilerHost({target: ts.ScriptTarget.ES5});

  const moduleResolver = bazelOpts.isJsTranspilation ?
      makeJsModuleResolver(bazelOpts.workspaceName) :
      ts.resolveModuleName;

  // Files which should be allowed to be read, but aren't TypeScript code
  const assets: string[] = [];
  if (bazelOpts.angularCompilerOptions) {
    if (bazelOpts.angularCompilerOptions.assets) {
      assets.push(...bazelOpts.angularCompilerOptions.assets);
    }
  }

  const tsickleCompilerHost = new CompilerHost(
      [...files, ...assets], options, bazelOpts, compilerHostDelegate, fileLoader,
      moduleResolver);
  let compilerHost: PluginCompilerHost = tsickleCompilerHost;
  const diagnosticPlugins: DiagnosticPlugin[] = [];

  let angularPlugin: EmitPlugin&DiagnosticPlugin|undefined;
  if (bazelOpts.angularCompilerOptions) {
    try {
      // Dynamically load the Angular emit plugin.
      // Lazy load, so that code that does not use the plugin doesn't even
      // have to spend the time to parse and load the plugin's source.
      const NgEmitPluginCtor = await getAngularEmitPluginOrThrow();
      const ngOptions = bazelOpts.angularCompilerOptions;

      // Add the rootDir setting to the options passed to NgTscPlugin.
      // Required so that synthetic files added to the rootFiles in the program
      // can be given absolute paths, just as we do in tsconfig.ts, matching
      // the behavior in TypeScript's tsconfig parsing logic.
      ngOptions['rootDir'] = options.rootDir;

      angularPlugin = new NgEmitPluginCtor(ngOptions);
    } catch (e) {
      return {
        diagnostics: [errorDiag(
            'when using `ts_library(use_angular_plugin=True)`, ' +
            `you must install "@angular/compiler-cli". Error: ${e}`)]
      };
    }

    diagnosticPlugins.push(angularPlugin);

    // Wrap host so that Ivy compiler can add a file to it (has synthetic types for checking templates)
    // TODO(arick): remove after ngsummary and ngfactory files eliminated
    compilerHost = angularPlugin!.wrapHost!(compilerHost, files, options);
  }


  const oldProgram = cache.getProgram(bazelOpts.target);
  const program = perfTrace.wrap(
      'createProgram',
      () => ts.createProgram(
          compilerHost.inputFiles, options, compilerHost, oldProgram));
  cache.putProgram(bazelOpts.target, program);

  let transformers: ts.CustomTransformers = {
    before: [],
    after: [],
    afterDeclarations: [],
  };

  let ignoreForDiagnostics = new Set<ts.SourceFile>();

  if (angularPlugin) {
    // The Angular plugin (via the `wrapHost` call above) inserts additional
    // "shim" files into the `ts.Program`, beyond the user's .ts files. For
    // proper operation, the plugin requires two modifications to the standard
    // flow of TypeScript compilation, relating to which files are either
    // type-checked or emitted.
    //
    // In tsc_wrapped, there is already a concept of which files should be
    // emitted (which is calculated from the compilation inputs, as well as any
    // paths that match expected Angular shims such as ngfactory files for those
    // inputs). So the `ignoreForEmit` set produced by the plugin can be
    // ignored here.

    const angularSetup = angularPlugin.setupCompilation(program);

    // Shims generated by the plugin do not benefit from normal type-checking,
    // for a few reasons.
    // 1) for emitted shims like ngfactory files, their proper contents are
    //    programmatically added via TypeScript transforms, so checking their
    //    initial contents is pointless and inefficient.
    // 2) for non-emitted shims like the ngtypecheck files used in template
    //    type-checking, they are managed and checked internally via the plugin
    //    `getDiagnostics` method. Checking them as part of the normal
    //    diagnostics flow will at best produce spurious, duplicate errors that
    //    are not reported in the correct context, and at worst can produce
    //    incorrect errors.
    //
    // The `ignoreForDiagnostics` set informs tsc_wrapped which Angular shim
    // files should be skipped when gathering diagnostics.
    ignoreForDiagnostics = angularSetup.ignoreForDiagnostics;

    transformers = angularPlugin.createTransformers();
  }

  for (const pluginConfig of options['plugins'] as ts.PluginImport[] || []) {
    if (pluginConfig.name === 'ts-lit-plugin') {
      const litTscPlugin =
          // Lazy load, so that code that does not use the plugin doesn't even
          // have to spend the time to parse and load the plugin's source.
          //
          // tslint:disable-next-line:no-require-imports
          new (require('ts-lit-plugin/lib/bazel-plugin').Plugin)(
              program, pluginConfig) as DiagnosticPlugin;
      diagnosticPlugins.push(litTscPlugin);
    }
  }

  if (!bazelOpts.isJsTranspilation) {
    // If there are any TypeScript type errors abort now, so the error
    // messages refer to the original source.  After any subsequent passes
    // (decorator downleveling or tsickle) we do not type check.
    let diagnostics = gatherDiagnostics(
        options, bazelOpts, program, disabledTsetseRules, diagnosticPlugins,
        ignoreForDiagnostics);
    if (!expectDiagnosticsWhitelist.length ||
        expectDiagnosticsWhitelist.some(p => bazelOpts.target.startsWith(p))) {
      diagnostics = bazelDiagnostics.filterExpected(
          bazelOpts, diagnostics, bazelDiagnostics.uglyFormat);
    } else if (bazelOpts.expectedDiagnostics.length > 0) {
      diagnostics.push(errorDiag(
          `Only targets under ${
              expectDiagnosticsWhitelist.join(', ')} can use ` +
          'expected_diagnostics, but got ' + bazelOpts.target));
    }

    // The Angular plugin creates a new program with template type-check information
    // This consumes (destroys) the old program so it's not suitable for re-use anymore
    // Ask Angular to give us the updated reusable program.
    if (angularPlugin) {
      cache.putProgram(bazelOpts.target, angularPlugin.getNextProgram!());
    }

    if (diagnostics.length > 0) {
      debug('compilation failed at', new Error().stack!);
      return {program, diagnostics};
    }
  }

  // Angular might have added files like input.ngfactory.ts or input.ngsummary.ts
  // and these need to be emitted.
  // TODO(arick): remove after Ivy is enabled and ngsummary/ngfactory files no longer needed
  function isAngularFile(sf: ts.SourceFile) {
    if (!/\.ng(factory|summary)\.ts$/.test(sf.fileName)) {
      return false;
    }
    const base = sf.fileName.slice(0, /*'.ngfactory|ngsummary.ts'.length*/ -13);
    // It's possible a file was named ngsummary.ts or ngfactory.ts but *not* synthetic
    // So verify that base.ts or base.tsx was originally part of the compilation
    const tsCandidate = {fileName: `${base}.ts`} as ts.SourceFile;
    const tsxCandidate = {fileName: `${base}.tsx`} as ts.SourceFile;
    return isCompilationTarget(bazelOpts, tsCandidate) ||
        isCompilationTarget(bazelOpts, tsxCandidate);
  }

  // If the Angular plugin is in use, this list of files to emit should exclude
  // any files defined in the `ignoreForEmit` set returned by the plugin.
  // However limiting the outputs to the set of compilation target files (plus
  // any Angular shims defined by `isAngularFile`) already has that effect, so
  // `ignoreForEmit` does not need to be factored in here.
  const compilationTargets = program.getSourceFiles().filter(
      sf => isCompilationTarget(bazelOpts, sf) || isAngularFile(sf));

  let diagnostics: ts.Diagnostic[] = [];
  let useTsickleEmit = bazelOpts.tsickle;

  if (useTsickleEmit) {
    diagnostics = emitWithTsickle(
        program, tsickleCompilerHost, compilationTargets, options, bazelOpts,
        transformers);
  } else {
    diagnostics = emitWithTypescript(program, compilationTargets, transformers);
  }

  if (diagnostics.length > 0) {
    debug('compilation failed at', new Error().stack!);
  }
  cache.printStats();
  return {program, diagnostics};
}