Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
Add CompilerSupplier support in compile-common
Browse files Browse the repository at this point in the history
  • Loading branch information
g. nicholas d'andrea committed Aug 13, 2021
1 parent d52234e commit f88640d
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 0 deletions.
35 changes: 35 additions & 0 deletions packages/compile-common/src/compilerSupplier/constructor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export namespace Constructor {
/**
* Type-level description for a particular constructor
*/
export type Specification = {
/**
* Options argument for specified constructor
*/
options: any;

/**
* Output object created by specified constructor
*/
result: any;
};

/**
* Getter type for constructor arg
*/
export type Options<S extends Specification> = S["options"];

/**
* Getter type for constructed object
*/
export type Result<S extends Specification> = S["result"];
}

/**
* An object of type Constructor<S> is a JS constructor (or "class") that
* takes a sole argument (of the specified "options" type), and instantiates
* a new object in memory (of the specified "result" type).
*/
export type Constructor<S extends Constructor.Specification> = new (
options: Constructor.Options<S>
) => Constructor.Result<S>;
3 changes: 3 additions & 0 deletions packages/compile-common/src/compilerSupplier/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { Results } from "./results";
export { Strategy, BaseStrategy } from "./strategy";
export { Supplier, forDefinition } from "./supplier";
9 changes: 9 additions & 0 deletions packages/compile-common/src/compilerSupplier/results.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export namespace Results {
export type Specification = {
load: any;
list: any;
};

export type Load<S extends Specification> = S["load"];
export type List<S extends Specification> = S["list"];
}
106 changes: 106 additions & 0 deletions packages/compile-common/src/compilerSupplier/strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import type { Constructor as FreeConstructor } from "./constructor";
import type { Results as FreeResults } from "./results";

export namespace Strategy {
/**
* Type-level description of a compiler supplier strategy
*/
export type Specification = {
constructor: Omit<FreeConstructor.Specification, "result">;
results: FreeResults.Specification;
allowsLoadingSpecificVersion: boolean;
allowsListingVersions: boolean;
};

/**
* Scoped re-exports of Constructor, using Strategy.Specification as
* generic param
*/
export namespace Constructor {
/**
* Getter for the specified constructor, intersected with fixed result
* type of Strategy<S>
*/
export type Specification<S extends Strategy.Specification> =
S["constructor"] & { result: Strategy<S>; };

/**
* Constructor options argument for a specified strategy
*/
export type Options<S extends Strategy.Specification> = FreeConstructor.Options<
Strategy.Constructor.Specification<S>
>;
}

/**
* Type representing a constructor for specified strategy
*/
export type Constructor<S extends Strategy.Specification> = FreeConstructor<
Strategy.Constructor.Specification<S>
>;

/**
* Results types for a particular strategy
*/
export namespace Results {
export type Specification<S extends Strategy.Specification> =
S["results"];

export type Load<S extends Strategy.Specification> = FreeResults.Load<
Strategy.Results.Specification<S>
>;

export type List<S extends Strategy.Specification> = FreeResults.List<
Strategy.Results.Specification<S>
>;
}

/**
* Getter type for whether specified strategy allows argument to load()
*/
export type AllowsLoadingSpecificVersion<
S extends Strategy.Specification
> = S["allowsLoadingSpecificVersion"];

/**
* Getter type for whether specified strategy provides version listing
*/
export type AllowsListingVersions<
S extends Strategy.Specification
> = S["allowsListingVersions"];

}

/**
* An object of type Strategy<S> provides version loading and possibly version
* listing functionality
*/
export type Strategy<S extends Strategy.Specification> = BaseStrategy<S> &
(true extends Strategy.AllowsLoadingSpecificVersion<S>
? {
load(version?: string): Strategy.Results.Load<S>;
}
: {
load(): Strategy.Results.Load<S>;
}) &
(true extends Strategy.AllowsListingVersions<S>
? {
list(): Strategy.Results.List<S>;
}
: {});

/**
* An object of BaseStrategy<S> allows loading an unspecified version, plus
* type guards to determine additional functionality
*/
export interface BaseStrategy<S extends Strategy.Specification> {
load(): Strategy.Results.Load<S>;

allowsLoadingSpecificVersion(): this is this & {
load(version?: string): Strategy.Results.Load<S>;
};

allowsListingVersions(): this is {
list(): Strategy.Results.List<S>;
};
}
139 changes: 139 additions & 0 deletions packages/compile-common/src/compilerSupplier/supplier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import type { Strategy as FreeStrategy, BaseStrategy } from "./strategy";
import type { Results as FreeResults } from "./results";

export namespace Supplier {
/**
* Type-level description of a supplier
*/
export type Specification = {
options: any;
results: FreeResults.Specification;
strategies: {
[strategyName: string]: Omit<FreeStrategy.Specification, "results">;
};
};

/**
* Getter type for the options argument passed to the constructor for one of
* the named strategies for a specified supplier
*/
export type Options<
S extends Supplier.Specification,
N extends Supplier.StrategyName<S>
> = S["options"] & Supplier.Strategy.Constructor.Options<S, N>;

export namespace Results {
/**
* Type-level specification of method results for specified supplier;
* used in conjunction with each strategy individually
*/
export type Specification<S extends Supplier.Specification> = S["results"];
}

export namespace Strategies {
export type Specification<S extends Supplier.Specification> = S["strategies"];
}

/**
* A type representing one of the strategy names for specified supplier
*/
export type StrategyName<S extends Supplier.Specification> = string &
keyof Supplier.Strategies.Specification<S>;

export namespace Strategy {
/**
* Type-level specification of one of the named strategies for specified
* supplier; joins specified supplier results type
*/
export type Specification<
S extends Supplier.Specification,
N extends Supplier.StrategyName<S>
> = Supplier.Strategies.Specification<S>[N] & {
results: Supplier.Results.Specification<S>
};

export namespace Constructor {
/**
* Type-level specification of the constructor for one of the named
* strategies for a specified supplier
*/
export type Specification<
S extends Supplier.Specification,
N extends Supplier.StrategyName<S>
> = FreeStrategy.Constructor<Supplier.Strategy.Specification<S, N>>;

/**
* Getter type for options argument passed to the constructor for one of
* the named strategies for a specified supplier
*/
export type Options<
S extends Supplier.Specification,
N extends Supplier.StrategyName<S>
> = {
[K in N]: FreeStrategy.Constructor.Options<
Supplier.Strategy.Specification<S, K>
>
}[N];
}

/**
* An object of Constructor<S, N> is a JS constructor (or "class") for one
* of the named strategies for specified supplier
*/
export type Constructor<
S extends Supplier.Specification,
N extends Supplier.StrategyName<S>
> = FreeStrategy.Constructor<Supplier.Strategy.Specification<S, N>>;
}

/**
* An object of type Definition<S> comprises constructors for each of the
* specified strategies, as well as a method to determine the name of which
* strategy to use for a given set of input options
*/
export type Definition<S extends Supplier.Specification> = {
determineStrategy(
options: Options<S, Supplier.StrategyName<S>>
): Supplier.StrategyName<S>;

strategyConstructors: {
[N in Supplier.StrategyName<S>]: Supplier.Strategy.Constructor<S, N>;
};
};
}

/**
* An object of type Supplier<S, N> provides an interface for loading and
* possibly listing versions of the compiler
*
* @dev for known strategy names, this computes the method signature of
* `load()` according to the corresponding strategy specification;
*
* for the default of "any strategy name", this type will resolve to an
* interface that may require the use of defined type guards
* (e.g. `if(supplier.allowsListingVersions()) { supplier.list(); }`)
*/
export type Supplier<
S extends Supplier.Specification,
N extends Supplier.StrategyName<S> = Supplier.StrategyName<S>
> = Supplier.StrategyName<S> extends N
? BaseStrategy<Supplier.Strategy.Specification<S, N>>
: FreeStrategy<Supplier.Strategy.Specification<S, N>>;

/**
* Given the definition of specified supplier, create a function that
* determines+constructs a supplier strategy for a given set of options.
*/
export const forDefinition = <S extends Supplier.Specification>({
determineStrategy,
strategyConstructors
}: Supplier.Definition<S>) => <N extends Supplier.StrategyName<S>>(
options: Supplier.Strategy.Constructor.Options<S, N>
): Supplier<S, N> => {
const strategyName = determineStrategy(options);
const Strategy = strategyConstructors[strategyName];

// @ts-ignore since we can't figure out N from inside
const supplier: Supplier<S, N> = new Strategy(options);
return supplier;
};
1 change: 1 addition & 0 deletions packages/compile-common/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { Profiler } from "./profiler";
export * as CompilerSupplier from "./compilerSupplier";
export * as Shims from "./shims";
export * as Sources from "./sources";
export * as Errors from "./errors";
Expand Down

0 comments on commit f88640d

Please sign in to comment.