Skip to content

Commit

Permalink
feat(json-mapper): create JsonMapperCompiler
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Aug 4, 2023
1 parent c38adf0 commit 701c801
Show file tree
Hide file tree
Showing 11 changed files with 762 additions and 2 deletions.
3 changes: 3 additions & 0 deletions packages/core/src/domain/Hooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
export class Hooks {
#listeners: Record<string, Function[]> = {};

has(event: string) {
return !!this.#listeners[event];
}
/**
* Listen a hook event
* @param event
Expand Down
102 changes: 102 additions & 0 deletions packages/specs/json-mapper/src/domain/JsonMapperCompiler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {isCollection, nameOf, objectKeys, Type} from "@tsed/core";
import {alterIgnore} from "@tsed/schema";
import {getRandomComponentId} from "../utils/getRandomComponentId";
import {getJsonMapperTypes} from "./JsonMapperTypesContainer";

export type JsonMapperCallback<Options> = (input: any, options?: Options) => any;
export type CachedJsonMapper<Options> = {
id: string;
fn: JsonMapperCallback<Options>;
source: string;
};

export abstract class JsonMapperCompiler<Options = any> {
cache = new Map<Type<any>, CachedJsonMapper<Options>>();
mappers: Record<string, JsonMapperCallback<Options>> = {};
schemes: Record<string, any> = {};

abstract alterValue(schemaId: string, value: any, options: Options): any;
abstract createMapper(model: Type<any>, id: string): string;

set(model: Type<any>, mapper: CachedJsonMapper<Options>) {
this.cache.set(model, mapper);
this.mappers[mapper.id] = mapper.fn;
}

get(model: Type<any>) {
return this.cache.get(model);
}

has(model: Type<any>) {
return this.cache.has(model);
}

addTypeMapper(model: Type<any>, fn: any) {
this.cache.set(model, {
id: nameOf(model),
source: "",
fn
});
this.mappers[nameOf(model)] = fn;

return this;
}

eval(id: string, model: Type<any>, mapper: string) {
const {cache} = this;
eval(`cache.set(model, { fn: ${mapper} })`);

const serializer = this.cache.get(model)!;
serializer.source = mapper;
serializer.id = id;

this.mappers[id] = serializer.fn;

return serializer.fn;
}

createContext(options: Options) {
return {
...options,
alterIgnore: (id: string, options: Options) => {
return alterIgnore(this.schemes[id], options);
},
alterValue: (id: string, value: any, options: Options) => {
return this.alterValue(id, value, options);
},
objectKeys,
cache: this.cache,
mappers: this.mappers
};
}

compile(model: Type<any>): CachedJsonMapper<Options> {
if (!this.has(model)) {
const types = getJsonMapperTypes();

if (types.has(model) && !isCollection(model)) {
const mapper = types.get(model);

if (mapper) {
this.addTypeMapper(model, mapper.serialize.bind(mapper));
}

return this.get(model)!;
}

const id = `${nameOf(model)}:${getRandomComponentId()}`;
this.cache.set(model, {id} as any);

const mapper = this.createMapper(model, id);

try {
this.eval(id, model, mapper);
} catch (err) {
console.log(mapper);
throw new Error(`Fail to compile mapper for ${nameOf(model)}. See the error above: ${err.message}.\n${mapper}`);
}
}

return this.get(model)!;
}
}
Loading

0 comments on commit 701c801

Please sign in to comment.