Skip to content

Commit

Permalink
chore: refactored TypeScript and generator implementation (#765)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonaslagoni authored Jun 15, 2022
1 parent 73ed585 commit 3d0a85d
Show file tree
Hide file tree
Showing 18 changed files with 355 additions and 639 deletions.
4 changes: 2 additions & 2 deletions src/generators/AbstractFileGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { CommonInputModel, OutputModel } from '../models';
import { InputMetaModel, OutputModel } from '../models';

export type FileGenerator = (content: string, toFile: string) => Promise<void>;

/**
* Abstract file generator which each file generator much implement.
*/
export interface AbstractFileGenerator<RenderCompleteModelOptions> {
generateToFiles(input: Record<string, unknown> | CommonInputModel, outputDirectory: string, options: RenderCompleteModelOptions): Promise<OutputModel[]>;
generateToFiles(input: Record<string, unknown> | InputMetaModel, outputDirectory: string, options: RenderCompleteModelOptions): Promise<OutputModel[]>;
}
60 changes: 35 additions & 25 deletions src/generators/AbstractGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CommonInputModel, CommonModel, OutputModel, Preset, Presets, RenderOutput, ProcessorOptions } from '../models';
import { InputMetaModel, OutputModel, Preset, Presets, RenderOutput, ProcessorOptions, MetaModel, ConstrainedMetaModel } from '../models';
import { InputProcessor } from '../processors';
import { IndentationTypes } from '../helpers';
import { isPresetWithOptions } from '../utils';
Expand All @@ -9,34 +9,33 @@ export interface CommonGeneratorOptions<P extends Preset = Preset> {
};
defaultPreset?: P;
presets?: Presets<P>;
processorOptions?: ProcessorOptions;
processorOptions?: ProcessorOptions
}

export const defaultGeneratorOptions: CommonGeneratorOptions = {
indentation: {
type: IndentationTypes.SPACES,
size: 2,
},
}
};

/**
* Abstract generator which must be implemented by each language
*/
export abstract class AbstractGenerator<Options extends CommonGeneratorOptions = CommonGeneratorOptions, RenderCompleteModelOptions = any> {
protected options: Options;

export abstract class AbstractGenerator<
Options extends CommonGeneratorOptions = CommonGeneratorOptions,
RenderCompleteModelOptions = any> {
constructor(
public readonly languageName: string,
defaultOptions?: Options,
passedOptions?: Options,
) {
this.options = this.mergeOptions(defaultOptions, passedOptions);
}
public readonly options: Options
) { }

public abstract render(model: CommonModel, inputModel: CommonInputModel): Promise<RenderOutput>;
public abstract renderCompleteModel(model: CommonModel, inputModel: CommonInputModel, options: RenderCompleteModelOptions): Promise<RenderOutput>;
public abstract render(model: MetaModel, inputModel: InputMetaModel): Promise<RenderOutput>;
public abstract renderCompleteModel(model: MetaModel, inputModel: InputMetaModel, options: RenderCompleteModelOptions): Promise<RenderOutput>;
public abstract constrainToMetaModel(model: MetaModel): ConstrainedMetaModel;
public abstract splitMetaModel(model: MetaModel): MetaModel[];

public process(input: Record<string, unknown>): Promise<CommonInputModel> {
public process(input: Record<string, unknown>): Promise<InputMetaModel> {
return InputProcessor.processor.process(input, this.options.processorOptions);
}

Expand All @@ -48,15 +47,16 @@ export abstract class AbstractGenerator<Options extends CommonGeneratorOptions =
* @param input
* @param options to use for rendering full output
*/
public async generateCompleteModels(input: Record<string, unknown> | CommonInputModel, options: RenderCompleteModelOptions): Promise<OutputModel[]> {
public async generateCompleteModels(input: Record<string, unknown> | InputMetaModel, options: RenderCompleteModelOptions): Promise<OutputModel[]> {
const inputModel = await this.processInput(input);
const renders = Object.values(inputModel.models).map(async (model) => {
const renderedOutput = await this.renderCompleteModel(model, inputModel, options);
const constrainedModel = this.constrainToMetaModel(model);
const renderedOutput = await this.renderCompleteModel(constrainedModel, inputModel, options);
return OutputModel.toOutputModel({
result: renderedOutput.result,
modelName: renderedOutput.renderedName,
dependencies: renderedOutput.dependencies,
model,
model: constrainedModel,
inputModel
});
});
Expand All @@ -68,31 +68,41 @@ export abstract class AbstractGenerator<Options extends CommonGeneratorOptions =
*
* @param input
*/
public async generate(input: Record<string, unknown> | CommonInputModel): Promise<OutputModel[]> {
public async generate(input: Record<string, unknown> | InputMetaModel): Promise<OutputModel[]> {
const inputModel = await this.processInput(input);
const renders = Object.values(inputModel.models).map(async (model) => {
const renderedOutput = await this.render(model, inputModel);
const constrainedModel = this.constrainToMetaModel(model);
const renderedOutput = await this.render(constrainedModel, inputModel);
return OutputModel.toOutputModel({
result: renderedOutput.result,
modelName: renderedOutput.renderedName,
dependencies: renderedOutput.dependencies,
model,
model: constrainedModel,
inputModel
});
});
return Promise.all(renders);
}

/**
* Process any of the input formats to the appropriate CommonInputModel type.
* Process any of the input formats to the appropriate InputMetaModel type and split out the meta models
* based on the requirements of the generators
*
* @param input
*/
private processInput(input: Record<string, unknown> | CommonInputModel): Promise<CommonInputModel> {
if (input instanceof CommonInputModel) {
return Promise.resolve(input);
private async processInput(input: Record<string, unknown> | InputMetaModel): Promise<InputMetaModel> {
const rawInputModel = input instanceof InputMetaModel ? input : await this.process(input);

//Split out the models based on the language specific requirements of which models is rendered separately
const splitOutModels: {[key: string]: MetaModel} = {};
for (const model of Object.values(rawInputModel.models)) {
const splitModels = this.splitMetaModel(model);
for (const splitModel of splitModels) {
splitOutModels[splitModel.name] = splitModel;
}
}
return this.process(input);
rawInputModel.models = splitOutModels;
return rawInputModel;
}

protected getPresets(presetType: string): Array<[Preset, unknown]> {
Expand Down
11 changes: 6 additions & 5 deletions src/generators/AbstractRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { AbstractGenerator, CommonGeneratorOptions } from './AbstractGenerator';
import { CommonModel, CommonInputModel, Preset } from '../models';
import { ConstrainedMetaModel, InputMetaModel, Preset } from '../models';
import { FormatHelpers, IndentationTypes } from '../helpers';

/**
* Abstract renderer with common helper methods
*/
export abstract class AbstractRenderer<
O extends CommonGeneratorOptions = CommonGeneratorOptions,
G extends AbstractGenerator = AbstractGenerator
G extends AbstractGenerator = AbstractGenerator,
RendererModelType extends ConstrainedMetaModel = ConstrainedMetaModel
> {
constructor(
readonly options: O,
protected readonly options: O,
readonly generator: G,
protected readonly presets: Array<[Preset, unknown]>,
protected readonly model: CommonModel,
protected readonly inputModel: CommonInputModel,
protected readonly model: RendererModelType,
protected readonly inputModel: InputMetaModel,
public dependencies: string[] = []
) {}

Expand Down
4 changes: 2 additions & 2 deletions src/generators/typescript/TypeScriptConstrainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { TypeMapping } from '../../helpers';
import { defaultEnumKeyConstraints, defaultEnumValueConstraints } from './constrainer/EnumConstrainer';
import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer';
import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer';
import { TypeScriptRenderer } from './TypeScriptRenderer';
import { TypeScriptOptions } from './TypeScriptGenerator';

export const TypeScriptDefaultTypeMapping: TypeMapping<TypeScriptRenderer> = {
export const TypeScriptDefaultTypeMapping: TypeMapping<TypeScriptOptions> = {
Object ({constrainedModel}): string {
return constrainedModel.name;
},
Expand Down
4 changes: 2 additions & 2 deletions src/generators/typescript/TypeScriptFileGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TypeScriptGenerator, TypeScriptRenderCompleteModelOptions } from './';
import { CommonInputModel, OutputModel } from '../../models';
import { InputMetaModel, OutputModel } from '../../models';
import * as path from 'path';
import { AbstractFileGenerator } from '../AbstractFileGenerator';
import { FileHelpers } from '../../helpers';
Expand All @@ -12,7 +12,7 @@ export class TypeScriptFileGenerator extends TypeScriptGenerator implements Abst
* @param outputDirectory where you want the models generated to
* @param options
*/
public async generateToFiles(input: Record<string, unknown> | CommonInputModel, outputDirectory: string, options?: TypeScriptRenderCompleteModelOptions): Promise<OutputModel[]> {
public async generateToFiles(input: Record<string, unknown> | InputMetaModel, outputDirectory: string, options?: TypeScriptRenderCompleteModelOptions): Promise<OutputModel[]> {
let generatedModels = await this.generateCompleteModels(input, options || {});
//Filter anything out that have not been successfully generated
generatedModels = generatedModels.filter((outputModel) => { return outputModel.modelName !== ''; });
Expand Down
Loading

0 comments on commit 3d0a85d

Please sign in to comment.