Skip to content

Commit

Permalink
feat: Remove Result object
Browse files Browse the repository at this point in the history
Closes #1238
  • Loading branch information
Gerrit0 committed Apr 12, 2020
1 parent b870905 commit 262a89c
Show file tree
Hide file tree
Showing 15 changed files with 136 additions and 308 deletions.
18 changes: 13 additions & 5 deletions src/lib/application.ts
Expand Up @@ -24,7 +24,7 @@ import {
DUMMY_APPLICATION_OWNER
} from './utils/component';
import { Options, BindOption } from './utils';
import { TypeDocAndTSOptions } from './utils/options/declaration';
import { TypeDocAndTSOptions, TypeDocOptions } from './utils/options/declaration';

/**
* The default TypeDoc main application class.
Expand Down Expand Up @@ -115,7 +115,13 @@ export class Application extends ChildableComponent<
* @param options The desired options to set.
*/
bootstrap(options: Partial<TypeDocAndTSOptions> = {}): { hasErrors: boolean, inputFiles: string[] } {
this.options.setValues(options); // Ignore result, plugins might declare an option
for (const [key, val] of Object.entries(options)) {
try {
this.options.setValue(key as keyof TypeDocOptions, val);
} catch {
// Ignore errors, plugins haven't been loaded yet and may declare an option.
}
}
this.options.read(new Logger());

const logger = this.loggerType;
Expand All @@ -130,11 +136,13 @@ export class Application extends ChildableComponent<
this.plugins.load();

this.options.reset();
this.options.setValues(options).mapErr(errors => {
for (const error of errors) {
for (const [key, val] of Object.entries(options)) {
try {
this.options.setValue(key as keyof TypeDocOptions, val);
} catch (error) {
this.logger.error(error.message);
}
});
}
this.options.read(this.logger);

return {
Expand Down
9 changes: 4 additions & 5 deletions src/lib/utils/index.ts
Expand Up @@ -4,7 +4,7 @@
* script will be used to switch this flag to false when publishing, then immediately back
* to true after a successful publish.
*/
type Strict = true;
type InternalOnly = true;

/**
* Helper type to convert `T` to `F` if strict mode is on.
Expand All @@ -21,14 +21,14 @@ type Strict = true;
* function over(flag: string): string { return flag }
* ```
*/
export type IfStrict<T, F> = Strict extends true ? T : F;
export type IfInternal<T, F> = InternalOnly extends true ? T : F;

/**
* Helper type to convert `T` to `never` if strict mode is on.
*
* See {@link IfStrict} for the rationale.
* See {@link IfInternal} for the rationale.
*/
export type NeverIfStrict<T> = IfStrict<never, T>;
export type NeverIfInternal<T> = IfInternal<never, T>;

export {
Options,
Expand All @@ -50,4 +50,3 @@ export {
} from './fs';
export { Logger, LogLevel, ConsoleLogger, CallbackLogger } from './loggers';
export { PluginHost } from './plugins';
export { Result } from './result';
29 changes: 14 additions & 15 deletions src/lib/utils/options/declaration.ts
@@ -1,6 +1,5 @@
import * as _ from 'lodash';
import { CompilerOptions } from 'typescript';
import { Result } from '../result';
import { IgnoredTsOptionKeys } from './sources/typescript';

/**
Expand Down Expand Up @@ -240,44 +239,44 @@ export type DeclarationOptionToOptionType<T extends DeclarationOption> =
* @param value
* @param option
*/
export function convert<T extends DeclarationOption>(value: unknown, option: T): Result<DeclarationOptionToOptionType<T>, string>;
export function convert<T extends DeclarationOption>(value: unknown, option: T): Result<unknown, string> {
export function convert<T extends DeclarationOption>(value: unknown, option: T): DeclarationOptionToOptionType<T>;
export function convert<T extends DeclarationOption>(value: unknown, option: T): unknown {
switch (option.type) {
case undefined:
case ParameterType.String:
return Result.Ok(value == null ? '' : String(value));
return value == null ? '' : String(value);
case ParameterType.Number:
return Result.Ok(parseInt(String(value), 10) || 0);
return parseInt(String(value), 10) || 0;
case ParameterType.Boolean:
return Result.Ok(Boolean(value));
return Boolean(value);
case ParameterType.Array:
if (Array.isArray(value)) {
return Result.Ok(value.map(String));
return value.map(String);
} else if (typeof value === 'string') {
return Result.Ok(value.split(','));
return value.split(',');
}
return Result.Ok([]);
return [];
case ParameterType.Map:
const optionMap = option as MapDeclarationOption<unknown>;
const key = String(value).toLowerCase();
if (optionMap.map instanceof Map) {
if (optionMap.map.has(key)) {
return Result.Ok(optionMap.map.get(key));
return optionMap.map.get(key);
}
if ([...optionMap.map.values()].includes(value)) {
return Result.Ok(value);
return value;
}
} else {
if (optionMap.map.hasOwnProperty(key)) {
return Result.Ok(optionMap.map[key]);
return optionMap.map[key];
}
if (Object.values(optionMap.map).includes(value)) {
return Result.Ok(value);
return value;
}
}
return Result.Err(optionMap.mapError ?? getMapError(optionMap.map, optionMap.name));
throw new Error(optionMap.mapError ?? getMapError(optionMap.map, optionMap.name));
case ParameterType.Mixed:
return Result.Ok(value);
return value;
}
}

Expand Down
80 changes: 26 additions & 54 deletions src/lib/utils/options/options.ts
Expand Up @@ -3,11 +3,10 @@ import * as ts from 'typescript';

import { DeclarationOption, ParameterScope, ParameterType, convert, TypeDocOptions, KeyToDeclaration, TypeDocAndTSOptions, TypeDocOptionMap } from './declaration';
import { Logger } from '../loggers';
import { Result, Ok, Err } from '../result';
import { insertPrioritySorted } from '../array';
import { addTSOptions, addTypeDocOptions } from './sources';
import { Application } from '../../..';
import { NeverIfStrict } from '..';
import { NeverIfInternal } from '..';

/**
* Describes an option reader that discovers user configuration and converts it to the
Expand Down Expand Up @@ -149,7 +148,7 @@ export class Options {
* Adds an option declaration to the container.
* @param declaration
*/
addDeclaration(declaration: NeverIfStrict<Readonly<DeclarationOption>>): void;
addDeclaration(declaration: NeverIfInternal<Readonly<DeclarationOption>>): void;

addDeclaration(declaration: Readonly<DeclarationOption>): void {
const names = [declaration.name];
Expand Down Expand Up @@ -224,7 +223,7 @@ export class Options {
* @param name
*/
isDefault(name: keyof TypeDocOptions): boolean;
isDefault(name: NeverIfStrict<string>): boolean;
isDefault(name: NeverIfInternal<string>): boolean;
isDefault(name: string): boolean {
// getValue will throw if the declaration does not exist.
return this.getValue(name as keyof TypeDocOptions) === this.getDeclaration(name)!.defaultValue;
Expand All @@ -242,32 +241,18 @@ export class Options {
* @param name
*/
getValue<K extends keyof TypeDocOptions>(name: K): TypeDocOptions[K];
getValue(name: NeverIfStrict<string>): unknown;
getValue(name: NeverIfInternal<string>): unknown;
getValue(name: string): unknown {
return this.tryGetValue(name as keyof TypeDocOptions).match({
ok: v => v,
err(err) { throw err; }
});
}

/**
* Tries to get the given option key, returns an [[Ok]] result if the option has been
* declared with a TypeDoc scope, or an [[Err]] result otherwise.
* @param name
*/
tryGetValue<K extends keyof TypeDocOptions>(name: K): Result<TypeDocOptions[K], Error>;
tryGetValue(name: NeverIfStrict<string>): Result<unknown, Error>;
tryGetValue(name: string): Result<unknown, Error> {
const declaration = this.getDeclaration(name);
if (!declaration) {
return Err(new Error(`Unknown option '${name}'`));
throw new Error(`Unknown option '${name}'`);
}

if (declaration.scope === ParameterScope.TypeScript) {
return Err(new Error('TypeScript options must be fetched with getCompilerOptions.'));
throw new Error('TypeScript options must be fetched with getCompilerOptions.');
}

return Ok(this._values[declaration.name]);
return this._values[declaration.name];
}

/**
Expand All @@ -278,61 +263,48 @@ export class Options {
}

/**
* Sets the given declared option. Returns a result with the Err set if the option fails,
* otherwise Ok(void).
* Sets the given declared option. Throws if setting the option fails.
* @param name
* @param value
*/
setValue<K extends keyof TypeDocAndTSOptions>(name: K, value: TypeDocAndTSOptions[K]): Result<void, Error>;
setValue(name: NeverIfStrict<string>, value: NeverIfStrict<unknown>): Result<void, Error>;
setValue(name: string, value: unknown): Result<void, Error> {
setValue<K extends keyof TypeDocAndTSOptions>(name: K, value: TypeDocAndTSOptions[K]): void;
setValue(name: NeverIfInternal<string>, value: NeverIfInternal<unknown>): void;
setValue(name: string, value: unknown): void {
const declaration = this.getDeclaration(name);
if (!declaration) {
return Err(Error(`Tried to set an option (${name}) that was not declared.`));
throw new Error(`Tried to set an option (${name}) that was not declared.`);
}

return convert(value, declaration).match({
ok: value => {
const bag = declaration.scope === ParameterScope.TypeScript
? this._compilerOptions
: this._values;
bag[declaration.name] = value;
return Ok(void 0);
},
err: err => Err(Error(err))
});
const converted = convert(value, declaration);
const bag = declaration.scope === ParameterScope.TypeScript
? this._compilerOptions
: this._values;
bag[declaration.name] = converted;
}

/**
* Sets all the given option values, returns a result with an array of errors for
* keys which failed to be set.
* Sets all the given option values, throws if any value fails to be set.
* @param obj
* @deprecated will be removed in 0.19. Use setValue in a loop instead.
*/
setValues(obj: Partial<TypeDocAndTSOptions>): Result<void, Error[]> {
const errors: Error[] = [];
setValues(obj: NeverIfInternal<Partial<TypeDocAndTSOptions>>): void {
this._logger.warn('Options.setValues is deprecated and will be removed in 0.19.');
for (const [name, value] of Object.entries(obj)) {
this.setValue(name as keyof TypeDocOptions, value).match({
ok() {},
err(error) {
errors.push(error);
}
});
this.setValue(name as keyof TypeDocOptions, value);
}
return errors.length ? Err(errors) : Ok(void 0);
}

/**
* Sets the value of a given option to its default value.
* @param declaration The option whoes value should be reset.
* @param declaration The option whose value should be reset.
*/
private setOptionValueToDefault(declaration: Readonly<DeclarationOption>): void {
if (declaration.scope !== ParameterScope.TypeScript) {
// No nead to convert the defaultValue for a map type as it has to be of a specific type
// No need to convert the defaultValue for a map type as it has to be of a specific type
if (declaration.type === ParameterType.Map) {
this._values[declaration.name] = declaration.defaultValue;
} else {
this._values[declaration.name] = convert(declaration.defaultValue, declaration)
.expect(`Failed to validate default value for ${declaration.name}`);
this._values[declaration.name] = convert(declaration.defaultValue, declaration);
}
}
}
Expand All @@ -356,7 +328,7 @@ export function BindOption<K extends keyof TypeDocOptionMap>(name: K):
* @privateRemarks
* This overload is intended for plugin use only with looser type checks. Do not use internally.
*/
export function BindOption(name: NeverIfStrict<string>):
export function BindOption(name: NeverIfInternal<string>):
(target: { application: Application } | { options: Options }, key: PropertyKey) => void;

export function BindOption(name: string) {
Expand Down
22 changes: 12 additions & 10 deletions src/lib/utils/options/readers/arguments.ts
@@ -1,7 +1,6 @@
import { OptionsReader, Options } from '..';
import { Logger } from '../../loggers';
import { ParameterType } from '../declaration';
import { Result } from '../..';

/**
* Obtains option values from command-line arguments
Expand All @@ -19,13 +18,19 @@ export class ArgumentsReader implements OptionsReader {
read(container: Options, logger: Logger): void {
// Make container's type more lax, we do the appropriate checks manually.
const options = container as Options & {
setValue(name: string, value: unknown): Result<void, Error>;
setValue(name: string, value: unknown): void;
getValue(name: string): unknown;
};
const seen = new Set<string>();
let index = 0;

const error = (error: Error) => logger.error(error.message);
const trySet = (name: string, value: unknown) => {
try {
options.setValue(name, value);
} catch (err) {
logger.error(err.message);
}
};

while (index < this.args.length) {
const name = this.args[index];
Expand All @@ -35,22 +40,19 @@ export class ArgumentsReader implements OptionsReader {

if (decl) {
if (seen.has(decl.name) && decl.type === ParameterType.Array) {
options.setValue(
decl.name,
(options.getValue(decl.name) as string[]).concat(this.args[index])
).mapErr(error);
trySet(decl.name, (options.getValue(decl.name) as string[]).concat(this.args[index]));
} else if (decl.type === ParameterType.Boolean) {
const value = String(this.args[index]).toLowerCase();

if (value === 'true' || value === 'false') {
options.setValue(decl.name, value === 'true').mapErr(error);
trySet(decl.name, value === 'true');
} else {
options.setValue(decl.name, true).mapErr(error);
trySet(decl.name, true);
// Bool option didn't consume the next argument as expected.
index--;
}
} else {
options.setValue(decl.name, this.args[index]).mapErr(error);
trySet(decl.name, this.args[index]);
}
seen.add(decl.name);
} else {
Expand Down
24 changes: 14 additions & 10 deletions src/lib/utils/options/readers/tsconfig.ts
Expand Up @@ -37,7 +37,7 @@ export class TSConfigReader implements OptionsReader {
this._tryReadOptions(tsconfigOpt, container);
}

private _tryReadOptions(file: string, container: Options, logger?: Logger): void {
private _tryReadOptions(file: string, container: Options & { setValue(name: string, value: unknown): void }, logger?: Logger): void {
let fileToRead: string | undefined = file;
if (!isFile(fileToRead)) {
fileToRead = ts.findConfigFile(file, isFile, file.toLowerCase().endsWith('.json') ? basename(file) : undefined);
Expand All @@ -58,7 +58,7 @@ export class TSConfigReader implements OptionsReader {
{},
fileToRead);

container.setValue('inputFiles', fileNames).unwrap();
container.setValue('inputFiles', fileNames);
for (const key of IGNORED) {
delete options[key];
}
Expand All @@ -71,16 +71,20 @@ export class TSConfigReader implements OptionsReader {
delete typedocOptions.options;
}

container.setValues(options).mapErr(errors => {
for (const err of errors) {
logger?.error(err.message);
for (const [key, val] of Object.entries(options)) {
try {
container.setValue(key, val);
} catch (error) {
logger?.error(error.message);
}
});
container.setValues(typedocOptions || {}).mapErr(errors => {
for (const err of errors) {
logger?.error(err.message);
}
for (const [key, val] of Object.entries(typedocOptions || {})) {
try {
container.setValue(key, val);
} catch (error) {
logger?.error(error.message);
}
});
}
}

}

0 comments on commit 262a89c

Please sign in to comment.