Skip to content

Commit 68d5cbd

Browse files
committed
Fix handling of noEmit/emitDeclarationOnly in watch mode
Closes #1716 (again)
1 parent fbf198e commit 68d5cbd

File tree

3 files changed

+99
-16
lines changed

3 files changed

+99
-16
lines changed

src/lib/application.ts

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
writeFile,
1414
discoverNpmPlugins,
1515
NeverIfInternal,
16+
TSConfigReader,
1617
} from "./utils/index";
1718

1819
import {
@@ -23,13 +24,13 @@ import {
2324
import { Options, BindOption } from "./utils";
2425
import type { TypeDocOptions } from "./utils/options/declaration";
2526
import { flatMap, unique } from "./utils/array";
26-
import { basename } from "path";
2727
import { validateExports } from "./validation/exports";
2828
import { ok } from "assert";
2929
import {
3030
DocumentationEntryPoint,
3131
EntryPointStrategy,
3232
getEntryPoints,
33+
getWatchEntryPoints,
3334
} from "./utils/entry-point";
3435
import { nicePath } from "./utils/paths";
3536

@@ -297,24 +298,17 @@ export class Application extends ChildableComponent<
297298
return;
298299
}
299300

300-
// Matches the behavior of the tsconfig option reader.
301-
let tsconfigFile = this.options.getValue("tsconfig");
302-
tsconfigFile =
303-
ts.findConfigFile(
304-
tsconfigFile,
305-
ts.sys.fileExists,
306-
tsconfigFile.toLowerCase().endsWith(".json")
307-
? basename(tsconfigFile)
308-
: undefined
309-
) ?? "tsconfig.json";
301+
const tsconfigFile =
302+
TSConfigReader.findConfigFile(this.options.getValue("tsconfig")) ??
303+
"tsconfig.json";
310304

311305
// We don't want to do it the first time to preserve initial debug status messages. They'll be lost
312306
// after the user saves a file, but better than nothing...
313307
let firstStatusReport = true;
314308

315309
const host = ts.createWatchCompilerHost(
316310
tsconfigFile,
317-
this.options.fixCompilerOptions({}),
311+
{},
318312
ts.sys,
319313
ts.createEmitAndSemanticDiagnosticsBuilderProgram,
320314
(diagnostic) => this.logger.diagnostic(diagnostic),
@@ -343,8 +337,20 @@ export class Application extends ChildableComponent<
343337
}
344338

345339
if (successFinished) {
340+
if (
341+
this.options.getValue("emit") === "both" ||
342+
this.options.getValue("emit") === true
343+
) {
344+
currentProgram.emit();
345+
}
346+
346347
this.logger.resetErrors();
347-
const entryPoints = this.getEntryPoints();
348+
this.logger.resetWarnings();
349+
const entryPoints = getWatchEntryPoints(
350+
this.logger,
351+
this.options,
352+
currentProgram
353+
);
348354
if (!entryPoints) {
349355
return;
350356
}
@@ -358,6 +364,30 @@ export class Application extends ChildableComponent<
358364
}
359365
};
360366

367+
const origCreateProgram = host.createProgram;
368+
host.createProgram = (
369+
rootNames,
370+
options,
371+
host,
372+
oldProgram,
373+
configDiagnostics,
374+
references
375+
) => {
376+
// If we always do this, we'll get a crash the second time a program is created.
377+
if (rootNames !== undefined) {
378+
options = this.options.fixCompilerOptions(options || {});
379+
}
380+
381+
return origCreateProgram(
382+
rootNames,
383+
options,
384+
host,
385+
oldProgram,
386+
configDiagnostics,
387+
references
388+
);
389+
};
390+
361391
const origAfterProgramCreate = host.afterProgramCreate;
362392
host.afterProgramCreate = (program) => {
363393
if (ts.getPreEmitDiagnostics(program.getProgram()).length === 0) {

src/lib/utils/entry-point.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,45 @@ export function getEntryPoints(
7878
return result;
7979
}
8080

81+
export function getWatchEntryPoints(
82+
logger: Logger,
83+
options: Options,
84+
program: ts.Program
85+
): DocumentationEntryPoint[] | undefined {
86+
let result: DocumentationEntryPoint[] | undefined;
87+
88+
const entryPoints = options.getValue("entryPoints");
89+
switch (options.getValue("entryPointStrategy")) {
90+
case EntryPointStrategy.Resolve:
91+
result = getEntryPointsForPaths(logger, entryPoints, options, [
92+
program,
93+
]);
94+
break;
95+
96+
case EntryPointStrategy.Expand:
97+
result = getExpandedEntryPointsForPaths(
98+
logger,
99+
entryPoints,
100+
options,
101+
[program]
102+
);
103+
break;
104+
105+
case EntryPointStrategy.Packages:
106+
logger.error(
107+
"Watch mode does not support 'packages' style entry points."
108+
);
109+
break;
110+
}
111+
112+
if (result && result.length === 0) {
113+
logger.error("Unable to find any entry points.");
114+
return;
115+
}
116+
117+
return result;
118+
}
119+
81120
function getModuleName(fileName: string, baseDir: string) {
82121
return normalizePath(relative(baseDir, fileName)).replace(
83122
/(\/index)?(\.d)?\.[tj]sx?$/,

src/lib/utils/options/readers/tsconfig.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,29 @@ export class TSConfigReader implements OptionsReader {
2424

2525
name = "tsconfig-json";
2626

27-
read(container: Options, logger: Logger): void {
28-
const file = container.getValue("tsconfig");
29-
27+
/**
28+
* Not considered part of the public API. You can use it, but it might break.
29+
* @internal
30+
*/
31+
static findConfigFile(file: string): string | undefined {
3032
let fileToRead: string | undefined = file;
3133
if (isDir(fileToRead)) {
3234
fileToRead = ts.findConfigFile(file, isFile);
3335
}
3436

3537
if (!fileToRead || !isFile(fileToRead)) {
38+
return;
39+
}
40+
41+
return fileToRead;
42+
}
43+
44+
read(container: Options, logger: Logger): void {
45+
const file = container.getValue("tsconfig");
46+
47+
let fileToRead = TSConfigReader.findConfigFile(file);
48+
49+
if (!fileToRead) {
3650
// If the user didn't give us this option, we shouldn't complain about not being able to find it.
3751
if (container.isSet("tsconfig")) {
3852
logger.error(`The tsconfig file ${file} does not exist`);

0 commit comments

Comments
 (0)