diff --git a/src/decorators/Attribute.ts b/src/decorators/Attribute.ts index a4026ad..26d42c3 100644 --- a/src/decorators/Attribute.ts +++ b/src/decorators/Attribute.ts @@ -1,9 +1,12 @@ import PropertyDecorator from '../contracts/PropertyDecorator' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' import Field from './Field' /** * Create a attribute decorator. */ -export default function Attribute (value: any = null): PropertyDecorator { - return Field(model => model.attr(value)) +export function Attribute (value: FunctorOrValue = null): PropertyDecorator { + return Field(model => model.attr(unwrapFunctorOrValue(value))) } + +export default Attribute diff --git a/src/decorators/BelongsTo.ts b/src/decorators/BelongsTo.ts new file mode 100644 index 0000000..32f8239 --- /dev/null +++ b/src/decorators/BelongsTo.ts @@ -0,0 +1,13 @@ +import PropertyDecorator from '@/contracts/PropertyDecorator' +import { Model } from '@vuex-orm/core' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' +import Field from './Field' + +export function BelongsTo ( + parent: FunctorOrValue, + foreignKey: FunctorOrValue, ownerKey?: FunctorOrValue +): PropertyDecorator { + return Field(model => (model.belongsTo as any)(...[parent, foreignKey, ownerKey].map(unwrapFunctorOrValue))) +} + +export default BelongsTo diff --git a/src/decorators/BelongsToMany.ts b/src/decorators/BelongsToMany.ts new file mode 100644 index 0000000..6a6a7aa --- /dev/null +++ b/src/decorators/BelongsToMany.ts @@ -0,0 +1,15 @@ +import PropertyDecorator from '@/contracts/PropertyDecorator' +import { Model } from '@vuex-orm/core' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' +import Field from './Field' + +export function BelongsToMany ( + related: FunctorOrValue, + pivot: FunctorOrValue, + foreignPivotKey: FunctorOrValue, relatedPivotKey: FunctorOrValue, + parentKey?: FunctorOrValue, relatedKey?: FunctorOrValue +): PropertyDecorator { + return Field(model => (model.belongsToMany as any)(...[related, pivot, foreignPivotKey, relatedPivotKey, parentKey, relatedKey].map(unwrapFunctorOrValue))) +} + +export default BelongsToMany diff --git a/src/decorators/Bool.ts b/src/decorators/Bool.ts index 9bb8a08..f755229 100644 --- a/src/decorators/Bool.ts +++ b/src/decorators/Bool.ts @@ -1,10 +1,13 @@ import PropertyDecorator from '../contracts/PropertyDecorator' import { TypeOptions } from '../options/Options' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' import Primitive from './Primitive' /** * Create a bool decorator. */ -export default function Bool (value: boolean | null, options?: TypeOptions): PropertyDecorator { - return Primitive(model => model.boolean(value), options) +export function Bool (value?: FunctorOrValue | null, options?: TypeOptions): PropertyDecorator { + return Primitive(model => model.boolean(unwrapFunctorOrValue(value)), options) } + +export default Bool diff --git a/src/decorators/DecoratedModel.ts b/src/decorators/DecoratedModel.ts new file mode 100644 index 0000000..a259dfa --- /dev/null +++ b/src/decorators/DecoratedModel.ts @@ -0,0 +1,41 @@ +import { Model } from '@vuex-orm/core' +import InheritanceTypes from '@vuex-orm/core/lib/model/contracts/InheritanceTypes' + +// Creates an optional class decorator to be used as means to add static fields +export function DecoratedModel ( + entityName: string, + options?: { + parentEntity?: string, + types?: InheritanceTypes, + typeKey?: string + } +): (target: any) => any | void { + return (target: any): any | void => { + const model = target.constructor as typeof Model + + // Do this temporarily until upstream vuex-orm declare this field officially + // I want to use this to notify hot reloader in the future + ;(model as any).isDecorated = true + + model.entity = entityName + + // generate base entity key assignment + if (options?.parentEntity) { + model.baseEntity = options.parentEntity + } + + // generate type discriminator + if (options?.types) { + model.types = () => options.types! + + // assignment type key for this entity + if (options?.typeKey) { + model.typeKey = options.typeKey + } + } + + return target + } +} + +export default DecoratedModel diff --git a/src/decorators/Field.ts b/src/decorators/Field.ts index c86b4a3..6a3659f 100644 --- a/src/decorators/Field.ts +++ b/src/decorators/Field.ts @@ -1,12 +1,12 @@ -import { Model, Attribute } from '@vuex-orm/core' +import { Attribute, Model } from '@vuex-orm/core' import PropertyDecorator from '../contracts/PropertyDecorator' -type Callback = (model: typeof Model) => Attribute +type Callback = (model: typeof Model, propertyKey: string) => Attribute /** * Create a generic field decorator. */ -export default function Field (callback: Callback): PropertyDecorator { +export function Field (callback: Callback): PropertyDecorator { return (target: Model, propertyKey: string): void => { const model = target.constructor as typeof Model @@ -18,6 +18,8 @@ export default function Field (callback: Callback): PropertyDecorator { model.cachedFields[model.entity] = {} } - model.cachedFields[model.entity][propertyKey] = callback(model) + model.cachedFields[model.entity][propertyKey] = callback(model, propertyKey) } } + +export default Field diff --git a/src/decorators/HasMany.ts b/src/decorators/HasMany.ts new file mode 100644 index 0000000..6740f27 --- /dev/null +++ b/src/decorators/HasMany.ts @@ -0,0 +1,13 @@ +import PropertyDecorator from '@/contracts/PropertyDecorator' +import { Model } from '@vuex-orm/core' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' +import Field from './Field' + +export function HasMany ( + related: FunctorOrValue, foreignKey: FunctorOrValue, + localKey?: FunctorOrValue +): PropertyDecorator { + return Field(model => (model.hasMany as any)(...[related, foreignKey, localKey].map(unwrapFunctorOrValue))) +} + +export default HasMany diff --git a/src/decorators/HasManyBy.ts b/src/decorators/HasManyBy.ts new file mode 100644 index 0000000..638aec3 --- /dev/null +++ b/src/decorators/HasManyBy.ts @@ -0,0 +1,13 @@ +import PropertyDecorator from '@/contracts/PropertyDecorator' +import { Model } from '@vuex-orm/core' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' +import Field from './Field' + +export function HasManyBy ( + parent: FunctorOrValue, foreignKey: FunctorOrValue, + ownerKey?: FunctorOrValue +): PropertyDecorator { + return Field(model => (model.hasManyBy as any)(...[parent, foreignKey, ownerKey].map(unwrapFunctorOrValue))) +} + +export default HasManyBy diff --git a/src/decorators/HasManyThrough.ts b/src/decorators/HasManyThrough.ts new file mode 100644 index 0000000..185c745 --- /dev/null +++ b/src/decorators/HasManyThrough.ts @@ -0,0 +1,15 @@ +import PropertyDecorator from '@/contracts/PropertyDecorator' +import { Model } from '@vuex-orm/core' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' +import Field from './Field' + +export function HasManyThrough ( + related: FunctorOrValue, + through: FunctorOrValue, + firstKey: FunctorOrValue, secondKey: FunctorOrValue, + localKey?: FunctorOrValue, secondLocalKey?: FunctorOrValue +): PropertyDecorator { + return Field(model => (model.hasManyThrough as any)(...[related, through, firstKey, secondKey, localKey, secondLocalKey].map(unwrapFunctorOrValue))) +} + +export default HasManyThrough diff --git a/src/decorators/HasOne.ts b/src/decorators/HasOne.ts new file mode 100644 index 0000000..5714e53 --- /dev/null +++ b/src/decorators/HasOne.ts @@ -0,0 +1,13 @@ +import PropertyDecorator from '@/contracts/PropertyDecorator' +import { Model } from '@vuex-orm/core' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' +import Field from './Field' + +export function HasOne ( + related: FunctorOrValue, foreignKey: FunctorOrValue, + localKey?: FunctorOrValue +): PropertyDecorator { + return Field(model => (model.hasOne as any)(...[related, foreignKey, localKey].map(unwrapFunctorOrValue))) +} + +export default HasOne diff --git a/src/decorators/Increment.ts b/src/decorators/Increment.ts new file mode 100644 index 0000000..1737b08 --- /dev/null +++ b/src/decorators/Increment.ts @@ -0,0 +1,8 @@ +import PropertyDecorator from '../contracts/PropertyDecorator' +import Field from './Field' + +export function Increment (): PropertyDecorator { + return Field(model => model.increment()) +} + +export default Increment diff --git a/src/decorators/MorphMany.ts b/src/decorators/MorphMany.ts new file mode 100644 index 0000000..0e4e9cd --- /dev/null +++ b/src/decorators/MorphMany.ts @@ -0,0 +1,13 @@ +import PropertyDecorator from '@/contracts/PropertyDecorator' +import { Model } from '@vuex-orm/core' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' +import Field from './Field' + +export function MorphMany ( + related: FunctorOrValue, + id: FunctorOrValue, type: FunctorOrValue, localKey?: FunctorOrValue +): PropertyDecorator { + return Field(model => (model.morphMany as any)(...[related, id, type, localKey].map(unwrapFunctorOrValue))) +} + +export default MorphMany diff --git a/src/decorators/MorphOne.ts b/src/decorators/MorphOne.ts new file mode 100644 index 0000000..5677a31 --- /dev/null +++ b/src/decorators/MorphOne.ts @@ -0,0 +1,13 @@ +import PropertyDecorator from '@/contracts/PropertyDecorator' +import { Model } from '@vuex-orm/core' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' +import Field from './Field' + +export function MorphOne ( + related: FunctorOrValue, + id: FunctorOrValue, type: FunctorOrValue, localKey?: FunctorOrValue +): PropertyDecorator { + return Field(model => (model.morphOne as any)(...[related, id, type, localKey].map(unwrapFunctorOrValue))) +} + +export default MorphOne diff --git a/src/decorators/MorphTo.ts b/src/decorators/MorphTo.ts new file mode 100644 index 0000000..25bbb6c --- /dev/null +++ b/src/decorators/MorphTo.ts @@ -0,0 +1,11 @@ +import PropertyDecorator from '@/contracts/PropertyDecorator' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' +import Field from './Field' + +export function MorphTo ( + id: FunctorOrValue, type: FunctorOrValue +): PropertyDecorator { + return Field(model => (model.morphTo as any)(...[id, type].map(unwrapFunctorOrValue))) +} + +export default MorphTo diff --git a/src/decorators/MorphToMany.ts b/src/decorators/MorphToMany.ts new file mode 100644 index 0000000..839f175 --- /dev/null +++ b/src/decorators/MorphToMany.ts @@ -0,0 +1,15 @@ +import PropertyDecorator from '@/contracts/PropertyDecorator' +import { Model } from '@vuex-orm/core' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' +import Field from './Field' + +export function MorphToMany ( + related: FunctorOrValue, + pivot: FunctorOrValue, + relatedId: FunctorOrValue, id: FunctorOrValue, type: FunctorOrValue, + parentKey?: FunctorOrValue, relatedKey?: FunctorOrValue +): PropertyDecorator { + return Field(model => (model.morphToMany as any)(...[related, pivot, relatedId, id, type, parentKey, relatedKey].map(unwrapFunctorOrValue))) +} + +export default MorphToMany diff --git a/src/decorators/MorphedByMany.ts b/src/decorators/MorphedByMany.ts new file mode 100644 index 0000000..fbcce54 --- /dev/null +++ b/src/decorators/MorphedByMany.ts @@ -0,0 +1,15 @@ +import PropertyDecorator from '@/contracts/PropertyDecorator' +import { Model } from '@vuex-orm/core' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' +import Field from './Field' + +export function MorphedByMany ( + related: FunctorOrValue, + pivot: FunctorOrValue, + relatedId: FunctorOrValue, id: FunctorOrValue, type: FunctorOrValue, + parentKey?: FunctorOrValue, relatedKey?: FunctorOrValue +): PropertyDecorator { + return Field(model => (model.morphedByMany as any)(...[related, pivot, relatedId, id, type, parentKey, relatedKey].map(unwrapFunctorOrValue))) +} + +export default MorphedByMany diff --git a/src/decorators/Num.ts b/src/decorators/Num.ts index d42f93b..340baab 100644 --- a/src/decorators/Num.ts +++ b/src/decorators/Num.ts @@ -1,10 +1,13 @@ import PropertyDecorator from '../contracts/PropertyDecorator' import { TypeOptions } from '../options/Options' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' import Primitive from './Primitive' /** * Create a num decorator. */ -export default function Num (value: number | null, options?: TypeOptions): PropertyDecorator { - return Primitive(model => model.number(value), options) +export function Num (value?: FunctorOrValue | null, options?: TypeOptions): PropertyDecorator { + return Primitive(model => model.number(unwrapFunctorOrValue(value)), options) } + +export default Num diff --git a/src/decorators/PrimaryKey.ts b/src/decorators/PrimaryKey.ts new file mode 100644 index 0000000..619b72f --- /dev/null +++ b/src/decorators/PrimaryKey.ts @@ -0,0 +1,21 @@ +import PropertyDecorator from '@/contracts/PropertyDecorator' +import { Model } from '@vuex-orm/core' + +export function PrimaryKey (): PropertyDecorator { + return (target: Model, propertyKey: string): void => { + const model = target.constructor as typeof Model + + // making the primary key values an array + // even if you have one and only one value there's no effect in functionality + if (!model.primaryKey) { + model.primaryKey = [] + } else if (typeof model.primaryKey === 'string') { + const oldPrimaryKey = model.primaryKey + model.primaryKey = [oldPrimaryKey] + } + + model.primaryKey.push(propertyKey) + } +} + +export default PrimaryKey diff --git a/src/decorators/Primitive.ts b/src/decorators/Primitive.ts index a5a05ad..7236778 100644 --- a/src/decorators/Primitive.ts +++ b/src/decorators/Primitive.ts @@ -8,20 +8,27 @@ type Callback = (model: typeof Model) => Type /** * Create a generic type decorator. */ -export default function Primitive (callback: Callback, options?: TypeOptions): PropertyDecorator { - return Field((model) => { +export function Primitive (callback: Callback, options?: TypeOptions): PropertyDecorator { + return Field((model, propertyKey) => { const type = callback(model) - if (type.value === null && !options?.nullable) { - throw new Error( - "[Vuex ORM] You've defined the default value of a field as `null` " + - 'without enabling `nullable` option. If you want the field to accept' + - '`null`, set `nullable` option to `true`.' - ) + if ( + typeof type?.value !== 'number' // if that number is 0, what will happen?! always remember it was coerced to false + && !(type?.value || options?.nullable) + ) { + throw new Error(` +[Vuex ORM] You've defined the default value of a field as \`null\` without enabling \`nullable\` option. +If you want the field to accept\`null\`, set \`nullable\` option to \`true\`. +Problematic class name: ${model.name}; Related property key: ${propertyKey} + `) } - options?.nullable && type.nullable() + if (options?.nullable) { + type.nullable() + } return type }) } + +export default Primitive diff --git a/src/decorators/Str.ts b/src/decorators/Str.ts index fcc2d88..f7643bc 100644 --- a/src/decorators/Str.ts +++ b/src/decorators/Str.ts @@ -1,10 +1,13 @@ import PropertyDecorator from '../contracts/PropertyDecorator' import { TypeOptions } from '../options/Options' +import { FunctorOrValue, unwrapFunctorOrValue } from '../utils' import Primitive from './Primitive' /** * Create a str decorator. */ -export default function Str (value: string | null, options?: TypeOptions): PropertyDecorator { - return Primitive(model => model.string(value), options) +export function Str (value?: FunctorOrValue | null, options?: TypeOptions): PropertyDecorator { + return Primitive(model => model.string(unwrapFunctorOrValue(value)), options) } + +export default Str diff --git a/src/decorators/index.ts b/src/decorators/index.ts new file mode 100644 index 0000000..400ced3 --- /dev/null +++ b/src/decorators/index.ts @@ -0,0 +1,20 @@ +export { Attribute } from './Attribute' +export { BelongsTo } from './BelongsTo' +export { BelongsToMany } from './BelongsToMany' +export { Bool } from './Bool' +export { DecoratedModel } from './DecoratedModel' +export { Field } from './Field' +export { HasMany } from './HasMany' +export { HasManyBy } from './HasManyBy' +export { HasManyThrough } from './HasManyThrough' +export { HasOne } from './HasOne' +export { Increment } from './Increment' +export { MorphedByMany } from './MorphedByMany' +export { MorphMany } from './MorphMany' +export { MorphOne } from './MorphOne' +export { MorphTo } from './MorphTo' +export { MorphToMany } from './MorphToMany' +export { Num } from './Num' +export { PrimaryKey } from './PrimaryKey' +export { Primitive } from './Primitive' +export { Str } from './Str' diff --git a/src/index.cjs.ts b/src/index.cjs.ts index e8fba20..5202f38 100644 --- a/src/index.cjs.ts +++ b/src/index.cjs.ts @@ -1,9 +1,27 @@ -import Attribute from './decorators/Attribute' -import Str from './decorators/Str' -import Bool from './decorators/Bool' +import { + Attribute, BelongsTo, BelongsToMany, Bool, DecoratedModel, Field, HasMany, HasManyBy, HasManyThrough, HasOne, Increment, MorphedByMany, MorphMany, MorphOne, MorphTo, + MorphToMany, Num, PrimaryKey, Primitive, Str +} from './decorators' export default { Attribute, - Str, - Bool + BelongsTo, + BelongsToMany, + Bool, + DecoratedModel, + Field, + HasMany, + HasManyBy, + HasManyThrough, + HasOne, + Increment, + MorphedByMany, + MorphMany, + MorphOne, + MorphTo, + MorphToMany, + Num, + PrimaryKey, + Primitive, + Str } diff --git a/src/index.ts b/src/index.ts index a22294b..d59f282 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,50 @@ -import Attribute from './decorators/Attribute' -import Str from './decorators/Str' -import Bool from './decorators/Bool' +import { + Attribute, BelongsTo, BelongsToMany, Bool, DecoratedModel, Field, HasMany, HasManyBy, HasManyThrough, HasOne, Increment, MorphedByMany, MorphMany, MorphOne, MorphTo, + MorphToMany, Num, PrimaryKey, Primitive, Str +} from './decorators' export { Attribute, - Str, - Bool + BelongsTo, + BelongsToMany, + Bool, + DecoratedModel, + Field, + HasMany, + HasManyBy, + HasManyThrough, + HasOne, + Increment, + MorphedByMany, + MorphMany, + MorphOne, + MorphTo, + MorphToMany, + Num, + PrimaryKey, + Primitive, + Str } export default { Attribute, - Str, - Bool + BelongsTo, + BelongsToMany, + Bool, + Field, + DecoratedModel, + HasMany, + HasManyBy, + HasManyThrough, + HasOne, + Increment, + MorphedByMany, + MorphMany, + MorphOne, + MorphTo, + MorphToMany, + Num, + PrimaryKey, + Primitive, + Str } diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..556d4fa --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,4 @@ +export type FunctorOrValue = (() => T) | T + +export const unwrapFunctorOrValue = (functorOrValue: FunctorOrValue): T => + ((functorOrValue instanceof Function && functorOrValue()) || functorOrValue) as T diff --git a/test/unit/Attribute.spec.ts b/test/unit/Attribute.spec.ts index 419fb47..6bbef07 100644 --- a/test/unit/Attribute.spec.ts +++ b/test/unit/Attribute.spec.ts @@ -1,8 +1,9 @@ import { Model, Attr } from '@vuex-orm/core' -import Attribute from '@/decorators/Attribute' +import { Attribute, DecoratedModel } from '@/decorators' -describe('Attribute', () => { - it('can define `attr` field', () => { +describe('attribute', () => { + it('can define `attr` field', function () { + @DecoratedModel('users') class User extends Model { @Attribute() id!: number @@ -12,7 +13,7 @@ describe('Attribute', () => { expect((new User()).id).toBe(null) }) - it('can define `attr` field with default value', () => { + it('can define `attr` field with default value', function () { class User extends Model { @Attribute(1) id!: number diff --git a/test/unit/Bool.spec.ts b/test/unit/Bool.spec.ts index 2080377..129c53c 100644 --- a/test/unit/Bool.spec.ts +++ b/test/unit/Bool.spec.ts @@ -1,8 +1,9 @@ import { Model, Boolean } from '@vuex-orm/core' -import Bool from '@/decorators/Bool' +import { DecoratedModel, Bool } from '@/decorators' -describe('Bool', () => { - it('can define `bool` field', () => { +describe('boolean', () => { + it('can define `bool` field', function () { + @DecoratedModel('users') class User extends Model { @Bool(true) active!: boolean @@ -12,10 +13,11 @@ describe('Bool', () => { expect((new User()).active).toBe(true) }) - it('can define `bool` field as nullable', () => { + it('can define `bool` field as nullable', function () { + @DecoratedModel('users') class User extends Model { @Bool(null, { nullable: true }) - active!: boolean + active?: boolean } expect(User.getFields().active).toBeInstanceOf(Boolean) diff --git a/test/unit/Num.spec.ts b/test/unit/Num.spec.ts index 85a8747..4fccbf2 100644 --- a/test/unit/Num.spec.ts +++ b/test/unit/Num.spec.ts @@ -1,8 +1,9 @@ import { Model, Number } from '@vuex-orm/core' -import Num from '@/decorators/Num' +import { Num, DecoratedModel } from '@/decorators' -describe('Num', () => { - it('can define `number` field', () => { +describe('number', () => { + it('can define `number` field', function () { + @DecoratedModel('users') class User extends Model { @Num(34) age!: number @@ -12,10 +13,11 @@ describe('Num', () => { expect((new User()).age).toBe(34) }) - it('can define `number` as nullable', () => { + it('can define `number` as nullable', function () { + @DecoratedModel('users') class User extends Model { @Num(null, { nullable: true }) - age!: number + age?: number } expect(User.getFields().age).toBeInstanceOf(Number) diff --git a/test/unit/Primitive.spec.ts b/test/unit/Primitive.spec.ts index d074820..c0cb042 100644 --- a/test/unit/Primitive.spec.ts +++ b/test/unit/Primitive.spec.ts @@ -1,9 +1,10 @@ import { Model } from '@vuex-orm/core' -import Str from '@/decorators/Str' +import { Str, DecoratedModel } from '@/decorators' -describe('Primitive', () => { - it('throws if default value is `null` without `nullable` option is set to `true`', () => { +describe('primitive', () => { + it('throws if default value is `null` without `nullable` option is set to `true`', function () { expect(() => { + @DecoratedModel('users') class User extends Model { @Str(null) name!: string diff --git a/test/unit/Str.spec.ts b/test/unit/Str.spec.ts index c0af789..66944e8 100644 --- a/test/unit/Str.spec.ts +++ b/test/unit/Str.spec.ts @@ -1,8 +1,9 @@ +import { DecoratedModel, Str } from '@/decorators' import { Model, String } from '@vuex-orm/core' -import Str from '@/decorators/Str' -describe('Str', () => { - it('can define `string` field', () => { +describe('string', () => { + it('can define `string` field', function () { + @DecoratedModel('users') class User extends Model { @Str('John Doe') name!: string @@ -16,13 +17,14 @@ describe('Str', () => { expect((new User()).email).toBe('john.doe@example.com') }) - it('can define `string` as nullable', () => { + it('can define `string` as nullable', function () { + @DecoratedModel('users') class User extends Model { @Str(null, { nullable: true }) - name!: string + name?: string @Str(null, { nullable: true }) - email!: string + email?: string } expect(User.getFields().name).toBeInstanceOf(String) diff --git a/test/unit/cyclic/Cyclic.spec.ts b/test/unit/cyclic/Cyclic.spec.ts new file mode 100644 index 0000000..da7c130 --- /dev/null +++ b/test/unit/cyclic/Cyclic.spec.ts @@ -0,0 +1,11 @@ +import { BelongsTo, HasMany } from '@vuex-orm/core' +import { EntityA, EntityB, EntityC } from './fixtures' + +describe('cyclic', function () { + it('should run perfectly', function () { + expect(EntityA.getFields().manyB).toBeInstanceOf(HasMany) + expect(EntityB.getFields().oneA).toBeInstanceOf(BelongsTo) + expect(EntityB.getFields().cS).toBeInstanceOf(HasMany) + expect(EntityC.getFields().bS).toBeInstanceOf(HasMany) + }) +}) diff --git a/test/unit/cyclic/fixtures/EntityA.ts b/test/unit/cyclic/fixtures/EntityA.ts new file mode 100644 index 0000000..49f1b1f --- /dev/null +++ b/test/unit/cyclic/fixtures/EntityA.ts @@ -0,0 +1,16 @@ +import { DecoratedModel, HasMany, Num, PrimaryKey, Str } from '@/decorators' +import { Model } from '@vuex-orm/core' +import * as Entities from './' + +@DecoratedModel('A') +export class EntityA extends Model { + @PrimaryKey() + @Num(0) + public id!: number + + @Str('foo', { nullable: false }) + public foo!: string + + @HasMany(() => Entities.EntityB, 'id') + public manyB!: Entities.EntityB[] +} diff --git a/test/unit/cyclic/fixtures/EntityB.ts b/test/unit/cyclic/fixtures/EntityB.ts new file mode 100644 index 0000000..5945760 --- /dev/null +++ b/test/unit/cyclic/fixtures/EntityB.ts @@ -0,0 +1,19 @@ +import { BelongsTo, DecoratedModel, HasMany, Num, PrimaryKey, Str } from '@/decorators' +import { Model } from '@vuex-orm/core' +import * as Entities from './index' + +@DecoratedModel('A') +export class EntityB extends Model { + @PrimaryKey() + @Num(0) + public id!: number + + @Str(null, { nullable: true }) + public bar?: string + + @BelongsTo(() => Entities.EntityA, 'id') + public oneA!: Entities.EntityA + + @HasMany(() => Entities.EntityC, 'id', 'id') + public cS!: Entities.EntityC[] +} diff --git a/test/unit/cyclic/fixtures/EntityC.ts b/test/unit/cyclic/fixtures/EntityC.ts new file mode 100644 index 0000000..f65aaae --- /dev/null +++ b/test/unit/cyclic/fixtures/EntityC.ts @@ -0,0 +1,16 @@ +import { DecoratedModel, HasMany, Num, PrimaryKey, Str } from '@/decorators' +import { Model } from '@vuex-orm/core' +import * as Entities from './index' + +@DecoratedModel('A') +export class EntityC extends Model { + @PrimaryKey() + @Num(0) + public id!: number + + @Str('baz') + public baz!: string + + @HasMany(() => Entities.EntityB, 'id', 'id') + public bS!: Entities.EntityB[] +} diff --git a/test/unit/cyclic/fixtures/index.ts b/test/unit/cyclic/fixtures/index.ts new file mode 100644 index 0000000..4d333e9 --- /dev/null +++ b/test/unit/cyclic/fixtures/index.ts @@ -0,0 +1,3 @@ +export { EntityA } from './EntityA' +export { EntityB } from './EntityB' +export { EntityC } from './EntityC'