Skip to content

Commit

Permalink
feat: Support for --watch, --preserveWatchOutput, --emit
Browse files Browse the repository at this point in the history
  • Loading branch information
Gerrit0 committed Jan 25, 2021
1 parent c3a59be commit 2188f86
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 5 deletions.
18 changes: 18 additions & 0 deletions bin/typedoc
Expand Up @@ -58,6 +58,24 @@ async function run(app) {
return ExitCodes.NoEntryPoints;
}

if (app.options.getValue("watch")) {
app.convertAndWatch(async (project) => {
const out = app.options.getValue("out");
if (out) {
await app.generateDocs(project, out);
}
const json = app.options.getValue("json");
if (json) {
await app.generateJson(project, json);
}

if (!out && !json) {
await app.generateDocs(project, "./docs");
}
});
return ExitCodes.Ok;
}

const project = app.convert();
if (!project) {
return ExitCodes.CompileError;
Expand Down
122 changes: 122 additions & 0 deletions src/lib/application.ts
Expand Up @@ -24,6 +24,7 @@ import {
import { Options, BindOption } from "./utils";
import { TypeDocOptions } from "./utils/options/declaration";
import { flatMap } from "./utils/array";
import { basename } from "path";

// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageInfo = require("../../package.json") as {
Expand Down Expand Up @@ -238,12 +239,133 @@ export class Application extends ChildableComponent<
return;
}

if (this.application.options.getValue("emit")) {
for (const program of programs) {
program.emit();
}
}

return this.converter.convert(
this.expandInputFiles(this.entryPoints),
programs
);
}

public convertAndWatch(
success: (project: ProjectReflection) => Promise<void>
): void {
if (
!this.options.getValue("preserveWatchOutput") &&
this.logger instanceof ConsoleLogger
) {
ts.sys.clearScreen?.();
}

this.logger.verbose(
"Using TypeScript %s from %s",
this.getTypeScriptVersion(),
this.getTypeScriptPath()
);

if (
!supportedVersionMajorMinor.some(
(version) => version == ts.versionMajorMinor
)
) {
this.logger.warn(
`You are running with an unsupported TypeScript version! TypeDoc supports ${supportedVersionMajorMinor.join(
", "
)}`
);
}

if (Object.keys(this.options.getCompilerOptions()).length === 0) {
this.logger.warn(
`No compiler options set. This likely means that TypeDoc did not find your tsconfig.json. Generated documentation will probably be empty.`
);
}

// Doing this is considerably more complicated, we'd need to manage an array of programs, not convert until all programs
// have reported in the first time... just error out for now. I'm not convinced anyone will actually notice.
if (this.application.options.getFileNames().length === 0) {
this.logger.error(
"The provided tsconfig file looks like a solution style tsconfig, which is not supported in watch mode."
);
return;
}

// Matches the behavior of the tsconfig option reader.
let tsconfigFile = this.options.getValue("tsconfig");
tsconfigFile =
ts.findConfigFile(
tsconfigFile,
ts.sys.fileExists,
tsconfigFile.toLowerCase().endsWith(".json")
? basename(tsconfigFile)
: undefined
) ?? "tsconfig.json";

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

const host = ts.createWatchCompilerHost(
tsconfigFile,
{ noEmit: !this.application.options.getValue("emit") },
ts.sys,
ts.createEmitAndSemanticDiagnosticsBuilderProgram,
(diagnostic) => this.logger.diagnostic(diagnostic),
(status, newLine, _options, errorCount) => {
if (
!firstStatusReport &&
errorCount === void 0 &&
!this.options.getValue("preserveWatchOutput") &&
this.logger instanceof ConsoleLogger
) {
ts.sys.clearScreen?.();
}
firstStatusReport = false;
this.logger.write(
ts.flattenDiagnosticMessageText(status.messageText, newLine)
);
}
);

let successFinished = true;
let currentProgram: ts.Program | undefined;

const runSuccess = () => {
if (!currentProgram) {
return;
}

if (successFinished) {
this.logger.resetErrors();
const project = this.converter.convert(
this.expandInputFiles(this.entryPoints),
currentProgram
);
currentProgram = undefined;
successFinished = false;
success(project).then(() => {
successFinished = true;
runSuccess();
});
}
};

const origAfterProgramCreate = host.afterProgramCreate;
host.afterProgramCreate = (program) => {
if (ts.getPreEmitDiagnostics(program.getProgram()).length === 0) {
currentProgram = program.getProgram();
runSuccess();
}
origAfterProgramCreate?.(program);
};

ts.createWatchProgram(host);
}

/**
* Render HTML for the given project
*/
Expand Down
2 changes: 1 addition & 1 deletion src/lib/converter/converter.ts
Expand Up @@ -139,7 +139,7 @@ export class Converter extends ChildableComponent<
convert(
entryPoints: readonly string[],
programs: ts.Program | readonly ts.Program[]
): ProjectReflection | undefined {
): ProjectReflection {
programs = programs instanceof Array ? programs : [programs];
this.externalPatternCache = void 0;

Expand Down
4 changes: 4 additions & 0 deletions src/lib/utils/options/declaration.ts
Expand Up @@ -48,6 +48,10 @@ export interface TypeDocOptionMap {
includes: string;
media: string;

emit: boolean;
watch: boolean;
preserveWatchOutput: boolean;

out: string;
json: string;

Expand Down
4 changes: 0 additions & 4 deletions src/lib/utils/options/readers/arguments.ts
Expand Up @@ -16,10 +16,6 @@ export class ArgumentsReader implements OptionsReader {
}

read(container: Options, logger: Logger): void {
logger.verbose(
`Arguments reader reading with: ${JSON.stringify(this.args)}`
);

// Make container's type more lax, we do the appropriate checks manually.
const options = container as Options & {
setValue(name: string, value: unknown): void;
Expand Down
17 changes: 17 additions & 0 deletions src/lib/utils/options/sources/typedoc.ts
Expand Up @@ -84,6 +84,23 @@ export function addTypeDocOptions(options: Pick<Options, "addDeclaration">) {
hint: ParameterHint.Directory,
});

options.addDeclaration({
name: "watch",
help: "Watch files for changes and rebuild docs on change.",
type: ParameterType.Boolean,
});
options.addDeclaration({
name: "preserveWatchOutput",
help:
"If set, TypeDoc will not clear the screen between compilation runs.",
type: ParameterType.Boolean,
});
options.addDeclaration({
name: "emit",
help: "If set, TypeDoc will emit the TypeScript compilation result",
type: ParameterType.Boolean,
});

options.addDeclaration({
name: "out",
help: "Specifies the location the documentation should be written to.",
Expand Down

0 comments on commit 2188f86

Please sign in to comment.