diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a4020504f9c07..2201ad4822c38 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2428,10 +2428,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return visitEachChild(node, markAsSynthetic, /*context*/ undefined); } - function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken) { + function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken, skipCheck?: boolean) { // Ensure we have all the type information in place for this file so that all the // emitter questions of this resolver will return the right information. - getDiagnostics(sourceFile, cancellationToken); + getDiagnostics(sourceFile, cancellationToken, skipCheck); return emitResolver; } @@ -47483,10 +47483,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { tracing?.pop(); } - function checkSourceFile(node: SourceFile) { + function checkSourceFile(node: SourceFile, skipCheck: boolean | undefined) { tracing?.push(tracing.Phase.Check, "checkSourceFile", { path: node.path }, /*separateBeginAndEnd*/ true); performance.mark("beforeCheck"); - checkSourceFileWorker(node); + checkSourceFileWorker(node, skipCheck); performance.mark("afterCheck"); performance.measure("Check", "beforeCheck", "afterCheck"); tracing?.pop(); @@ -47511,10 +47511,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } // Fully type check a source file and collect the relevant diagnostics. - function checkSourceFileWorker(node: SourceFile) { + function checkSourceFileWorker(node: SourceFile, skipCheck: boolean | undefined) { const links = getNodeLinks(node); if (!(links.flags & NodeCheckFlags.TypeChecked)) { - if (skipTypeChecking(node, compilerOptions, host)) { + if (skipCheck || skipTypeChecking(node, compilerOptions, host)) { return; } @@ -47578,13 +47578,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function getDiagnostics(sourceFile: SourceFile, ct: CancellationToken): Diagnostic[] { + function getDiagnostics(sourceFile: SourceFile, ct: CancellationToken, skipCheck?: boolean): Diagnostic[] { try { // Record the cancellation token so it can be checked later on during checkSourceElement. // Do this in a finally block so we can ensure that it gets reset back to nothing after // this call is done. cancellationToken = ct; - return getDiagnosticsWorker(sourceFile); + return getDiagnosticsWorker(sourceFile, skipCheck); } finally { cancellationToken = undefined; @@ -47599,7 +47599,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { deferredDiagnosticsCallbacks = []; } - function checkSourceFileWithEagerDiagnostics(sourceFile: SourceFile) { + function checkSourceFileWithEagerDiagnostics(sourceFile: SourceFile, skipCheck?: boolean) { ensurePendingDiagnosticWorkComplete(); // then setup diagnostics for immediate invocation (as we are about to collect them, and // this avoids the overhead of longer-lived callbacks we don't need to allocate) @@ -47608,11 +47608,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // thus much more likely retaining the same union ordering as before we had lazy diagnostics) const oldAddLazyDiagnostics = addLazyDiagnostic; addLazyDiagnostic = cb => cb(); - checkSourceFile(sourceFile); + checkSourceFile(sourceFile, skipCheck); addLazyDiagnostic = oldAddLazyDiagnostics; } - function getDiagnosticsWorker(sourceFile: SourceFile): Diagnostic[] { + function getDiagnosticsWorker(sourceFile: SourceFile, skipCheck: boolean | undefined): Diagnostic[] { if (sourceFile) { ensurePendingDiagnosticWorkComplete(); // Some global diagnostics are deferred until they are needed and @@ -47621,7 +47621,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const previousGlobalDiagnostics = diagnostics.getGlobalDiagnostics(); const previousGlobalDiagnosticsSize = previousGlobalDiagnostics.length; - checkSourceFileWithEagerDiagnostics(sourceFile); + checkSourceFileWithEagerDiagnostics(sourceFile, skipCheck); const semanticDiagnostics = diagnostics.getDiagnostics(sourceFile.fileName); const currentGlobalDiagnostics = diagnostics.getGlobalDiagnostics(); @@ -47642,7 +47642,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Global diagnostics are always added when a file is not provided to // getDiagnostics - forEach(host.getSourceFiles(), checkSourceFileWithEagerDiagnostics); + forEach(host.getSourceFiles(), sourceFile => checkSourceFileWithEagerDiagnostics(sourceFile, skipCheck)); return diagnostics.getDiagnostics(); } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index dfb878c6265a0..9f8ba091c0fb5 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2870,7 +2870,11 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // This is because in the -out scenario all files need to be emitted, and therefore all // files need to be type checked. And the way to specify that all files need to be type // checked is to not pass the file to getEmitResolver. - const emitResolver = getTypeChecker().getEmitResolver(options.outFile ? undefined : sourceFile, cancellationToken); + const emitResolver = getTypeChecker().getEmitResolver( + options.outFile ? undefined : sourceFile, + cancellationToken, + forceDtsEmit && sourceFile && !canIncludeBindAndCheckDiagnsotics(sourceFile), + ); performance.mark("beforeEmit"); @@ -3006,14 +3010,12 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg const isJs = sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX; const isCheckJs = isJs && isCheckJsEnabledForFile(sourceFile, options); const isPlainJs = isPlainJsFile(sourceFile, options.checkJs); - const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false; // By default, only type-check .ts, .tsx, Deferred, plain JS, checked JS and External // - plain JS: .js files with no // ts-check and checkJs: undefined // - check JS: .js files with either // ts-check or checkJs: true // - external: files that are added by plugins - const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX - || sourceFile.scriptKind === ScriptKind.External || isPlainJs || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred); + const includeBindAndCheckDiagnostics = canIncludeBindAndCheckDiagnsotics(sourceFile); let bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray; let checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray; if (isPlainJs) { @@ -3025,6 +3027,20 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg }); } + function canIncludeBindAndCheckDiagnsotics(sourceFile: SourceFile) { + const isJs = sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX; + const isCheckJs = isJs && isCheckJsEnabledForFile(sourceFile, options); + const isPlainJs = isPlainJsFile(sourceFile, options.checkJs); + const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false; + + // By default, only type-check .ts, .tsx, Deferred, plain JS, checked JS and External + // - plain JS: .js files with no // ts-check and checkJs: undefined + // - check JS: .js files with either // ts-check or checkJs: true + // - external: files that are added by plugins + return !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX + || sourceFile.scriptKind === ScriptKind.External || isPlainJs || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred); + } + function getMergedBindAndCheckDiagnostics(sourceFile: SourceFile, includeBindAndCheckDiagnostics: boolean, ...allDiagnostics: (readonly Diagnostic[] | undefined)[]) { const flatDiagnostics = flatten(allDiagnostics); if (!includeBindAndCheckDiagnostics || !sourceFile.commentDirectives?.length) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index aae7c5494a17d..573b6846d682a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5264,7 +5264,7 @@ export interface TypeChecker { // Should not be called directly. Should only be accessed through the Program instance. /** @internal */ getDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; /** @internal */ getGlobalDiagnostics(): Diagnostic[]; - /** @internal */ getEmitResolver(sourceFile?: SourceFile, cancellationToken?: CancellationToken): EmitResolver; + /** @internal */ getEmitResolver(sourceFile?: SourceFile, cancellationToken?: CancellationToken, skipCheck?: boolean): EmitResolver; /** @internal */ getNodeCount(): number; /** @internal */ getIdentifierCount(): number;