Skip to content

Commit

Permalink
BREAKING CHANGE: Options may not be set once conversion starts. Enabl…
Browse files Browse the repository at this point in the history
…es a small perf improvement.
  • Loading branch information
Gerrit0 committed Jul 5, 2021
1 parent b04b3b0 commit f3294cd
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 13 deletions.
4 changes: 4 additions & 0 deletions src/lib/application.ts
Expand Up @@ -285,6 +285,9 @@ export class Application extends ChildableComponent<
* @returns An instance of ProjectReflection on success, undefined otherwise.
*/
public convert(): ProjectReflection | undefined {
// We seal here rather than in the Converter class since TypeDoc's tests reuse the Application
// with a few different settings.
this.options.seal();
this.logger.verbose(
`Using TypeScript ${this.getTypeScriptVersion()} from ${this.getTypeScriptPath()}`
);
Expand Down Expand Up @@ -355,6 +358,7 @@ export class Application extends ChildableComponent<
public convertAndWatch(
success: (project: ProjectReflection) => Promise<void>
): void {
this.options.seal();
if (
!this.options.getValue("preserveWatchOutput") &&
this.logger instanceof ConsoleLogger
Expand Down
2 changes: 1 addition & 1 deletion src/lib/converter/factories/comment.ts
Expand Up @@ -35,7 +35,7 @@ function getRootModuleDeclaration(node: ts.ModuleDeclaration): ts.Node {
node.parent &&
node.parent.kind === ts.SyntaxKind.ModuleDeclaration
) {
const parent = <ts.ModuleDeclaration>node.parent;
const parent = node.parent;
if (node.name.pos === parent.name.end + 1) {
node = parent;
} else {
Expand Down
12 changes: 7 additions & 5 deletions src/lib/converter/plugins/GroupPlugin.ts
Expand Up @@ -9,7 +9,8 @@ import { SourceDirectory } from "../../models/sources/directory";
import { Component, ConverterComponent } from "../components";
import { Converter } from "../converter";
import { Context } from "../context";
import { sortReflections } from "../../utils/sort";
import { sortReflections, SortStrategy } from "../../utils/sort";
import { BindOption } from "../../utils";

/**
* A handler that sorts and groups the found reflections in the resolving phase.
Expand Down Expand Up @@ -37,6 +38,10 @@ export class GroupPlugin extends ConverterComponent {
[ReflectionKind.TypeAlias]: "Type aliases",
};

/** @internal */
@BindOption("sort")
sortStrategies!: SortStrategy[];

/**
* Create a new GroupPlugin instance.
*/
Expand Down Expand Up @@ -92,10 +97,7 @@ export class GroupPlugin extends ConverterComponent {
reflection.children.length > 0 &&
!reflection.groups
) {
sortReflections(
reflection.children,
this.application.options.getValue("sort")
);
sortReflections(reflection.children, this.sortStrategies);
reflection.groups = GroupPlugin.getReflectionGroups(
reflection.children
);
Expand Down
2 changes: 1 addition & 1 deletion src/lib/serialization/schema.ts
Expand Up @@ -25,9 +25,9 @@
* ```
*
* For documentation on the JSON output properties, view the corresponding model.
* @module
*/

/** */
import * as M from "../models";
import { SourceReferenceWrapper, DecoratorWrapper } from "./serializers";

Expand Down
40 changes: 34 additions & 6 deletions src/lib/utils/options/options.ts
Expand Up @@ -85,6 +85,20 @@ export class Options {
this._logger = logger;
}

/**
* Marks the options as readonly, enables caching when fetching options, which improves performance.
*/
seal() {
Object.seal(this._values);
}

/**
* Checks if the options object has been sealed, preventing future changes to option values.
*/
isSealed() {
return Object.isSealed(this._values);
}

/**
* Sets the logger used when an option declaration fails to be added.
* @param logger
Expand Down Expand Up @@ -260,6 +274,12 @@ export class Options {
configPath?: NeverIfInternal<string>
): void;
setValue(name: string, value: unknown, configPath?: string): void {
if (this.isSealed()) {
throw new Error(
"Tried to modify an option value after options have been sealed."
);
}

const declaration = this.getDeclaration(name);
if (!declaration) {
throw new Error(
Expand Down Expand Up @@ -305,6 +325,12 @@ export class Options {
options: ts.CompilerOptions,
projectReferences: readonly ts.ProjectReference[] | undefined
) {
if (this.isSealed()) {
throw new Error(
"Tried to modify an option value after options have been sealed."
);
}

// We do this here instead of in the tsconfig reader so that API consumers which
// supply a program to `Converter.convert` instead of letting TypeDoc create one
// can just set the compiler options, and not need to know about this mapping.
Expand Down Expand Up @@ -353,13 +379,15 @@ export function BindOption(name: string) {
) {
Object.defineProperty(target, key, {
get(this: { application: Application } | { options: Options }) {
if ("options" in this) {
return this.options.getValue(name as keyof TypeDocOptions);
} else {
return this.application.options.getValue(
name as keyof TypeDocOptions
);
const options =
"options" in this ? this.options : this.application.options;
const value = options.getValue(name as keyof TypeDocOptions);

if (options.isSealed()) {
Object.defineProperty(this, key, { value });
}

return value;
},
enumerable: true,
configurable: true,
Expand Down
1 change: 1 addition & 0 deletions src/test/converter2.test.ts
Expand Up @@ -230,6 +230,7 @@ describe("Converter2", () => {
tsconfig: join(base, "tsconfig.json"),
plugin: [],
});
app.options.seal();

let program: ts.Program;
before("Compiles", () => {
Expand Down
53 changes: 53 additions & 0 deletions src/test/utils/options/options.test.ts
@@ -1,5 +1,6 @@
import { Logger, Options, ParameterType } from "../../../lib/utils";
import {
BindOption,
MapDeclarationOption,
NumberDeclarationOption,
} from "../../../lib/utils/options";
Expand Down Expand Up @@ -108,4 +109,56 @@ describe("Options", () => {

throws(() => options.isSet("does not exist" as never));
});

it("Throws if sealed and a value is set", () => {
const options = new Options(new Logger());
options.addDefaultDeclarations();
options.seal();

throws(() => options.setValue("emit", true));
throws(() => options.setCompilerOptions([], {}, []));
});
});

describe("BindOption", () => {
class Container {
constructor(public options: Options) {}

@BindOption("emit")
emit!: boolean;
}

it("Supports fetching options", () => {
const options = new Options(new Logger());
options.addDefaultDeclarations();

const container = new Container(options);
equal(container.emit, false);
});

it("Updates as option values change", () => {
const options = new Options(new Logger());
options.addDefaultDeclarations();

const container = new Container(options);
equal(container.emit, false);

options.setValue("emit", true);
equal(container.emit, true);
});

it("Caches set options when sealed", () => {
const options = new Options(new Logger());
options.addDefaultDeclarations();

const container = new Container(options);

options.setValue("emit", true);
options.seal();
equal(container.emit, true);

const prop = Object.getOwnPropertyDescriptor(container, "emit")!;
equal(prop.get, void 0);
equal(prop.value, true);
});
});

0 comments on commit f3294cd

Please sign in to comment.