Skip to content

Commit

Permalink
apply prettier to all files
Browse files Browse the repository at this point in the history
  • Loading branch information
hkan committed Feb 1, 2024
1 parent 3603c32 commit af20518
Show file tree
Hide file tree
Showing 11 changed files with 321 additions and 251 deletions.
149 changes: 86 additions & 63 deletions src/EntityBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,80 +1,103 @@
import { Entity, PartialPropsJson } from './Entity';
import { Buildable, Constructor } from './support/Type';
import { TypeMetadata } from './support/metadata/TypeMetadata';
import { defaultMetadataStorage } from './support/storage';
import { isEntityType } from './support/isEntityType';
import { StringHelper } from './support/StringHelper';
import { Entity, PartialPropsJson } from "./Entity";
import { Buildable, Constructor } from "./support/Type";
import { TypeMetadata } from "./support/metadata/TypeMetadata";
import { defaultMetadataStorage } from "./support/storage";
import { isEntityType } from "./support/isEntityType";
import { StringHelper } from "./support/StringHelper";

export class EntityBuilder {
public static buildOne<T extends Entity>(
buildClass: Constructor<T>,
sourceData: PartialPropsJson<T>,
): T {
const entity = new (buildClass)();
return entity.fromJson(sourceData);
}
public static buildOne<T extends Entity>(
buildClass: Constructor<T>,
sourceData: PartialPropsJson<T>,
): T {
const entity = new buildClass();
return entity.fromJson(sourceData);
}

public static buildMany<T extends Entity>(
buildClass: Constructor<T>,
sourceData: Array<PartialPropsJson<T>>,
): Array<T> {
return sourceData.map(
entityData => this.buildOne(buildClass, entityData),
);
}
public static buildMany<T extends Entity>(
buildClass: Constructor<T>,
sourceData: Array<PartialPropsJson<T>>,
): Array<T> {
return sourceData.map((entityData) =>
this.buildOne(buildClass, entityData),
);
}

public static fill<T extends Entity>(entity: T, data: PartialPropsJson<T>): T {
for (let key in data) {
EntityBuilder.fillProperty<T>(entity, key, data[key]);
}

return entity;
public static fill<T extends Entity>(
entity: T,
data: PartialPropsJson<T>,
): T {
for (let key in data) {
EntityBuilder.fillProperty<T>(entity, key, data[key]);
}

private static fillProperty<T extends Entity>(entity: T, key: string, value: any): void {
// Don't even bother for undefined values.
if (typeof value === 'undefined') {
return;
}
return entity;
}

const metadata: TypeMetadata = defaultMetadataStorage.findTypeMetadata(entity.constructor, key);
private static fillProperty<T extends Entity>(
entity: T,
key: string,
value: any,
): void {
// Don't even bother for undefined values.
if (typeof value === "undefined") {
return;
}

if (metadata) {
EntityBuilder.fillTypeDecoratedProperty<T>(entity, metadata, value);
return;
}
const metadata: TypeMetadata = defaultMetadataStorage.findTypeMetadata(
entity.constructor,
key,
);

// No type definition means scalar value, and we can just set that as is.
entity.setProp(StringHelper.toCamel(key), value);
if (metadata) {
EntityBuilder.fillTypeDecoratedProperty<T>(entity, metadata, value);
return;
}

private static fillTypeDecoratedProperty<T extends Entity>(entity: T, metadata: TypeMetadata, value: InstanceType<Buildable>) {
// We shouldn't copy objects to our entity, as the entity should be responsible for constructing these itself.
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
if (isEntityType(metadata.type)) {
entity.setProp(metadata.propertyName, EntityBuilder.buildOne(metadata.type, value));
} else {
entity.setProp(metadata.propertyName, new metadata.type(value))
}
// No type definition means scalar value, and we can just set that as is.
entity.setProp(StringHelper.toCamel(key), value);
}

return;
}
private static fillTypeDecoratedProperty<T extends Entity>(
entity: T,
metadata: TypeMetadata,
value: InstanceType<Buildable>,
) {
// We shouldn't copy objects to our entity, as the entity should be responsible for constructing these itself.
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
if (isEntityType(metadata.type)) {
entity.setProp(
metadata.propertyName,
EntityBuilder.buildOne(metadata.type, value),
);
} else {
entity.setProp(metadata.propertyName, new metadata.type(value));
}

// if we have an array, we check if it contains objects, in which case the entity itself should be assumed
// responsible to construct the array of entities.
if (Array.isArray(value) && value.length > 0) {
if (isEntityType(metadata.type)) {
entity.setProp(metadata.propertyName, EntityBuilder.buildMany(metadata.type, value));
} else {
entity.setProp(metadata.propertyName, value.map(item => new metadata.type(item)));
}
return;
}

return;
}
// if we have an array, we check if it contains objects, in which case the entity itself should be assumed
// responsible to construct the array of entities.
if (Array.isArray(value) && value.length > 0) {
if (isEntityType(metadata.type)) {
entity.setProp(
metadata.propertyName,
EntityBuilder.buildMany(metadata.type, value),
);
} else {
entity.setProp(
metadata.propertyName,
value.map((item) => new metadata.type(item)),
);
}

// Since all other scenarios have been exhausted, we're dealing with a primitive of some form.
// This can be an empty array of objects too, but since it's empty, there's no need for us
// to build an entity. As such, we can just assign it. The same goes for all primitives.
entity.setProp(metadata.propertyName, value);
return;
}

// Since all other scenarios have been exhausted, we're dealing with a primitive of some form.
// This can be an empty array of objects too, but since it's empty, there's no need for us
// to build an entity. As such, we can just assign it. The same goes for all primitives.
entity.setProp(metadata.propertyName, value);
}
}
13 changes: 9 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
export { Entity } from './Entity';
export type { Props, PropsJson, PartialProps, PartialPropsJson } from './Entity';
export { EntityBuilder } from './EntityBuilder';
export { Type } from './support/Type';
export { Entity } from "./Entity";
export type {
Props,
PropsJson,
PartialProps,
PartialPropsJson,
} from "./Entity";
export { EntityBuilder } from "./EntityBuilder";
export { Type } from "./support/Type";
12 changes: 6 additions & 6 deletions src/support/JsonExclude.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { defaultMetadataStorage } from './storage';
import {JsonExcludeMetadata} from './metadata/JsonExcludeMetadata';
import { defaultMetadataStorage } from "./storage";
import { JsonExcludeMetadata } from "./metadata/JsonExcludeMetadata";

export function JsonExclude() {
return function (target: any, key: string) {
const metadata = new JsonExcludeMetadata(target.constructor, key);
defaultMetadataStorage.addExcludeProperty(metadata);
};
return function (target: any, key: string) {
const metadata = new JsonExcludeMetadata(target.constructor, key);
defaultMetadataStorage.addExcludeProperty(metadata);
};
}
64 changes: 32 additions & 32 deletions src/support/StringHelper.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
export class StringHelper {
/**
* Convert a string to camelCase.
* @param source
* @returns {string}
*/
public static toCamel(source: string): string {
// Inspired by http://www.devcurry.com/2011/07/javascript-convert-camelcase-to-dashes.html
// Remove underscores and turn the next character into uppercase
const str: string = source.replace(/_+(.)/g, (x, chr) => chr.toUpperCase());
/**
* Convert a string to camelCase.
* @param source
* @returns {string}
*/
public static toCamel(source: string): string {
// Inspired by http://www.devcurry.com/2011/07/javascript-convert-camelcase-to-dashes.html
// Remove underscores and turn the next character into uppercase
const str: string = source.replace(/_+(.)/g, (x, chr) => chr.toUpperCase());

// If we have a leading uppercase (either the source started with an
// uppercase character or with an underscore), lowercase the first.
return this.lowercaseFirst(str);
}
// If we have a leading uppercase (either the source started with an
// uppercase character or with an underscore), lowercase the first.
return this.lowercaseFirst(str);
}

/**
* Convert a string to snake_case.
*
* @param source
* @returns {any}
*/
public static toSnake(source: string): string {
const str: string = this.lowercaseFirst(source);
return str.replace(/([A-Z])/g, x => '_' + x.toLowerCase());
}
/**
* Convert a string to snake_case.
*
* @param source
* @returns {any}
*/
public static toSnake(source: string): string {
const str: string = this.lowercaseFirst(source);
return str.replace(/([A-Z])/g, (x) => "_" + x.toLowerCase());
}

/**
* Lowercase the first letter of a string.
*
* @param str
* @returns {string}
*/
private static lowercaseFirst(str: string) {
return str.charAt(0).toLowerCase() + str.slice(1);
}
/**
* Lowercase the first letter of a string.
*
* @param str
* @returns {string}
*/
private static lowercaseFirst(str: string) {
return str.charAt(0).toLowerCase() + str.slice(1);
}
}
32 changes: 21 additions & 11 deletions src/support/Type.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { defaultMetadataStorage } from './storage';
import { StringHelper } from './StringHelper';
import { TypeMetadata } from './metadata/TypeMetadata';
import { Entity } from '../Entity';
import { defaultMetadataStorage } from "./storage";
import { StringHelper } from "./StringHelper";
import { TypeMetadata } from "./metadata/TypeMetadata";
import { Entity } from "../Entity";

export type Constructor<T> = { new(...args: any): T };
export type Constructor<T> = { new (...args: any): T };

// Types that can be passed as first argument to `EntityBuilder`.
export type Buildable = Constructor<Entity> | typeof Object | typeof String | typeof Number | typeof Boolean;
export type Buildable =
| Constructor<Entity>
| typeof Object
| typeof String
| typeof Number
| typeof Boolean;
export type DefaultExportedBuildable = { default: Buildable };
export type PackedBuildable = Buildable | DefaultExportedBuildable;

Expand All @@ -16,10 +21,15 @@ export type BuildableResolver = () => PackedBuildable;
export type Typeable = Buildable | BuildableResolver;

export function Type<T extends Typeable>(type: T, jsonKey?: string) {
return function (target: Entity, key: string) {
jsonKey = jsonKey ?? StringHelper.toSnake(key);
return function (target: Entity, key: string) {
jsonKey = jsonKey ?? StringHelper.toSnake(key);

const metadata = new TypeMetadata(target.constructor as Constructor<Entity>, key, jsonKey, type);
defaultMetadataStorage.addTypeMetadata(metadata);
};
const metadata = new TypeMetadata(
target.constructor as Constructor<Entity>,
key,
jsonKey,
type,
);
defaultMetadataStorage.addTypeMetadata(metadata);
};
}
10 changes: 6 additions & 4 deletions src/support/isEntityType.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Buildable, Constructor } from './Type';
import { Entity } from '../Entity';
import { Buildable, Constructor } from "./Type";
import { Entity } from "../Entity";

export function isEntityType(buildable: Buildable): buildable is Constructor<Entity> {
return buildable?.prototype instanceof Entity;
export function isEntityType(
buildable: Buildable,
): buildable is Constructor<Entity> {
return buildable?.prototype instanceof Entity;
}
5 changes: 4 additions & 1 deletion src/support/metadata/JsonExcludeMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export class JsonExcludeMetadata {
constructor(public target: Function, public propertyName: string) {}
constructor(
public target: Function,
public propertyName: string,
) {}
}
Loading

0 comments on commit af20518

Please sign in to comment.