Skip to content

Commit

Permalink
See #58. Add configuration options to dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
Glavin001 committed Apr 9, 2018
1 parent deaa761 commit c51e371
Show file tree
Hide file tree
Showing 11 changed files with 252 additions and 144 deletions.
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module.exports = {
"<rootDir>/node_modules/",
],
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
collectCoverage: true,
// collectCoverage: true,
coverageReporters: ["json", "lcov", "text", "html"],
coveragePathIgnorePatterns: [
"<rootDir>/__mocks__/",
Expand Down
30 changes: 20 additions & 10 deletions src/DependencyManager/Dependency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export abstract class Dependency {
private _version?: Version;
private _errors: Error[] = [];

constructor(protected options: DependencyOptions) {}
constructor(protected definition: DependencyDefinition, protected options: DependencyOptions) {}

public load(): Promise<boolean> {
if (this.isInstalled) {
Expand Down Expand Up @@ -44,7 +44,7 @@ export abstract class Dependency {
}

private versionFromText(text: string): string | undefined {
const { parseVersion } = this.options;
const { parseVersion } = this.definition;
if (!parseVersion) {
return text;
}
Expand Down Expand Up @@ -93,11 +93,11 @@ export abstract class Dependency {
}

public get name(): string {
return this.options.name;
return this.definition.name;
}

public get required(): boolean {
return !Boolean(this.options.optional);
return !Boolean(this.definition.optional);
}

public get version(): Version | undefined {
Expand All @@ -109,7 +109,7 @@ export abstract class Dependency {
}
}

export interface BaseDependencyOptions {
export interface BaseDependencyDefinition {
// tslint:disable-next-line:no-reserved-keywords
type: DependencyType;
name: string;
Expand All @@ -132,14 +132,14 @@ export type DependencyVersionParserFunction = (
text: string
) => string | undefined;

export interface NodeDependencyOptions extends BaseDependencyOptions {
export interface NodeDependencyDefinition extends BaseDependencyDefinition {
// tslint:disable-next-line:no-reserved-keywords
type: DependencyType.Node;
// tslint:disable-next-line:no-reserved-keywords
package: string;
}

export interface ExecutableDependencyOptions extends BaseDependencyOptions {
export interface ExecutableDependencyDefinition extends BaseDependencyDefinition {
// tslint:disable-next-line:no-reserved-keywords
type: DependencyType.Executable;
program: string;
Expand All @@ -149,6 +149,16 @@ export interface ExecutableDependencyOptions extends BaseDependencyOptions {
};
}

export type DependencyOptions =
| NodeDependencyOptions
| ExecutableDependencyOptions;
export type DependencyDefinition =
| NodeDependencyDefinition
| ExecutableDependencyDefinition;

export interface DependencyOptions {
// prefer_beautifier_config?: string;
// executables?: { [executableName: string]: ExecutableConfig };
path?: string;
}

// export interface ExecutableConfig {
// path?: string;
// }
14 changes: 7 additions & 7 deletions src/DependencyManager/DependencyFactory.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { Dependency, DependencyType, DependencyOptions } from "./Dependency";
import { Dependency, DependencyType, DependencyDefinition, DependencyOptions } from "./Dependency";
import { NodeDependency } from "./NodeDependency";
import { ExecutableDependency } from "./ExecutableDependency";

export class DependencyFactory {
constructor(private options: DependencyOptions) {}
constructor(private definition: DependencyDefinition, private options: DependencyOptions) {}

public dependency(): Dependency {
const { options } = this;
switch (options.type) {
const { definition, options } = this;
switch (definition.type) {
case DependencyType.Node:
return new NodeDependency(options);
return new NodeDependency(definition, options);
case DependencyType.Executable:
return new ExecutableDependency(options);
return new ExecutableDependency(definition, options);
default:
throw new Error(
`Dependency type not found for: ${JSON.stringify(options)}`
`Dependency type not found for: ${JSON.stringify(definition)}`
);
}
}
Expand Down
101 changes: 74 additions & 27 deletions src/DependencyManager/DependencyManager.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,94 @@
// tslint:disable:newspaper-order
// tslint:disable:no-reserved-keywords
import { DependencyFactory } from "./DependencyFactory";
import { Dependency, DependencyOptions } from "./Dependency";
import {
Dependency,
DependencyDefinition,
DependencyOptions,
} from "./Dependency";

export class DependencyManager {
private readonly dependencies: Dependency[];
private readonly lookup: {
[name: string]: Dependency;
};
private static registry: DependencyRegistry = {};

public static clearRegistry(): void {
this.registry = {};
}

constructor(
private beautifierName: string,
private dependencyDefinitions: DependencyDefinition[] = [],
private options: LanguageDependencyOptions = {}
) {
this.initializeDependencies();
}

private initializeDependencies(): void {
const lookup = DependencyManager.registry;
const beautifierLookup = lookup[this.beautifierName] || {};
lookup[this.beautifierName] = beautifierLookup;

constructor(dependencies: DependencyOptions[]) {
this.dependencies = dependencies.map(dependency =>
new DependencyFactory(dependency).dependency()
);
this.lookup = this.dependencies.reduce(
(lookup, dep) => ({
...lookup,
[dep.name]: dep,
}),
{}
);
this.dependencyDefinitions.forEach(def => {
const { name: dependencyName } = def;
const options = this.optionsForDependency(dependencyName);
const optionsKey = this.keyForOptions(options);
const depLookup = beautifierLookup[dependencyName] || {};
beautifierLookup[dependencyName] = depLookup;
depLookup[optionsKey] =
depLookup[optionsKey] || this.createDependency(def, options);
});
}

private optionsForDependency(dependencyName: string): DependencyOptions {
return this.options[dependencyName];
}

public has(name: string): boolean {
return Boolean(this.get(name));
}

public get<T extends Dependency>(name: string): T {
const dep = this.lookup[name] as T | undefined;
public load(): Promise<boolean> {
return Promise.all(
this.dependencyDefinitions
.map(def => this.get(def.name))
.map(dep => dep.load())
).then(() => true);
}

public get<T extends Dependency>(dependencyName: string): T {
const options = this.optionsForDependency(dependencyName);
const optionsKey = this.keyForOptions(options);
const lookup = this.registry[dependencyName] || {};
const dep = lookup[optionsKey] as T | undefined;
if (!dep) {
throw new Error(`Dependency with name ${name} not found.`);
throw new Error(`Dependency with name ${dependencyName} not found.`);
}
return dep;
}

public load(): Promise<boolean> {
return Promise.all(
this.dependencies.map(dep => {
return dep.load();
})
).then(() => true);
protected get registry(): DependencyRegistry[string] {
return DependencyManager.registry[this.beautifierName];
}

protected createDependency(
definition: DependencyDefinition,
options: DependencyOptions
): Dependency {
return new DependencyFactory(definition, options).dependency();
}

private keyForOptions(options: DependencyOptions = {}): string {
return JSON.stringify(options);
}
}

export interface DependencyMap {
[name: string]: Dependency;
export interface LanguageDependencyOptions {
[dependencyName: string]: DependencyOptions;
}

export interface DependencyRegistry {
[beautifierName: string]: {
[dependencyName: string]: {
[optionsKey: string]: Dependency;
};
};
}
27 changes: 21 additions & 6 deletions src/DependencyManager/ExecutableDependency.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { spawn, SpawnOptions } from "child_process";

import { Dependency, ExecutableDependencyOptions } from "./Dependency";
import {
Dependency,
ExecutableDependencyDefinition,
DependencyOptions,
} from "./Dependency";

export class ExecutableDependency extends Dependency {
constructor(protected options: ExecutableDependencyOptions) {
super(options);
constructor(
protected definition: ExecutableDependencyDefinition,
options: DependencyOptions = {}
) {
super(definition, options);
}

protected loadVersion() {
return this.run({ args: this.versionArgs }).then(({ stdout }) => stdout);
}

private get versionArgs(): string[] {
return this.options.versionArgs || ["--version"];
return this.definition.versionArgs || ["--version"];
}

public run({
Expand All @@ -25,7 +32,7 @@ export class ExecutableDependency extends Dependency {
stdin?: any;
}): Promise<RunResponse> {
return this.resolveArgs(args).then(finalArgs =>
this.spawn({ exe: this.program, args: finalArgs, options, stdin })
this.spawn({ exe: this.pathOrProgram, args: finalArgs, options, stdin })
);
}

Expand All @@ -35,8 +42,16 @@ export class ExecutableDependency extends Dependency {
);
}

private get pathOrProgram(): string {
return this.programPath || this.program;
}

private get program(): string {
return this.options.program;
return this.definition.program;
}

private get programPath(): string | undefined {
return this.options.path;
}

private spawn({
Expand Down
8 changes: 4 additions & 4 deletions src/DependencyManager/NodeDependency.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Dependency, NodeDependencyOptions } from "./Dependency";
import { Dependency, NodeDependencyDefinition, DependencyOptions } from "./Dependency";

// tslint:disable-next-line:no-require-imports no-var-requires
const requireg = require("requireg");

export class NodeDependency extends Dependency {
constructor(protected options: NodeDependencyOptions) {
super(options);
constructor(protected definition: NodeDependencyDefinition, options: DependencyOptions = {}) {
super(definition, options);
}

protected loadVersion() {
Expand Down Expand Up @@ -65,6 +65,6 @@ export class NodeDependency extends Dependency {
}

private get packageName(): string {
return this.options.package;
return this.definition.package;
}
}
Loading

0 comments on commit c51e371

Please sign in to comment.