Skip to content

Commit c64c6d1

Browse files
committed
feat(class-mock): add entity decorator、class decorator
1 parent 083710d commit c64c6d1

File tree

16 files changed

+515
-89
lines changed

16 files changed

+515
-89
lines changed

packages/class-mock/api.drawio.png

-44.5 KB
Binary file not shown.

packages/class-mock/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"build": "pnpm type-check &&esno ./scripts/build.ts",
1919
"build:watch": "cross-env WATCH=true pnpm build",
2020
"clean": "rimraf ./dist/**/*",
21-
"dev": "esno ./src/index.ts",
21+
"dev": "esno ./src/playground.ts",
2222
"test": "vitest run --silent --passWithNoTests",
2323
"test:watch": "pnpm test -- --watch",
2424
"type-check": "tsc --noEmit"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const CLASS_META_KEY = '__CLASS_MOCK_CLASS_META_KEY__'
2+
export const DEFAULT_ARRAY_LENGTH = 10
3+
export const DEFAULT_ARRAY_MAX = 50
4+
export const DEFAULT_ARRAY_MIN = 0

packages/class-mock/src/constants/metakey.constants.ts

Whitespace-only changes.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {CLASS_META_KEY} from '@/constants/meta.constants'
2+
import {ConfigDecorator} from './config.decorator'
3+
4+
const _decoratorProto = ConfigDecorator(
5+
{},
6+
{
7+
overridePropertyKey: CLASS_META_KEY,
8+
isClass: true
9+
}
10+
)
11+
12+
export const DefaultPartial = _decoratorProto.DefaultPartial
13+
export const DefaultInclude = _decoratorProto.DefaultInclude
14+
export const DefaultExclude = _decoratorProto.DefaultExclude
15+
export const DefaultAlwaysRandom = _decoratorProto.DefaultAlwaysRandom
16+
export const DefaultNotAlwaysRandom = _decoratorProto.DefaultNotAlwaysRandom

packages/class-mock/src/decorators/config.decorator.ts

Lines changed: 83 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,108 @@ import {
66
MockPropertyMetadata,
77
MockPropertyDecorator,
88
ArrayConfig,
9-
MockPropertyDecoratorProps
9+
MockPropertyDecoratorProps,
10+
MockClassDecorator,
11+
MockClassDecoratorProps
1012
} from '@/utils/types-helper'
1113

12-
export function ConfigDecorator<T extends MockPropertyDecoratorConfig = MockPropertyDecoratorConfig>(
13-
config: T = <T>{}
14-
): MockPropertyDecorator<T> {
15-
const createConfigDecorator = (config: T = <T>{}) => {
14+
export interface ConfigDecoratorOptions<IsClass extends boolean = false> {
15+
/**
16+
* for class decorator
17+
*/
18+
overridePropertyKey?: string
19+
20+
/**
21+
* for class decorator
22+
*/
23+
isClass?: IsClass
24+
}
25+
export function ConfigDecorator<
26+
T extends MockPropertyDecoratorConfig = MockPropertyDecoratorConfig,
27+
IsClass extends boolean = false
28+
>(
29+
config: T = <T>{},
30+
options: ConfigDecoratorOptions<IsClass> = {}
31+
): IsClass extends true ? MockClassDecorator<T> : MockPropertyDecorator<T> {
32+
const {overridePropertyKey, isClass} = options
33+
34+
const createConfigDecorator = <IsClass extends boolean = false>(
35+
config: T = <T>{}
36+
): IsClass extends true ? MockClassDecorator<T> : MockPropertyDecorator<T> => {
1637
const decorator = (target: any, propertyKey: string | symbol) => {
17-
const propertyName = propertyKey as string
38+
const propertyName = overridePropertyKey ? overridePropertyKey : (propertyKey as string)
1839
const _target = getTarget(target)
19-
const preMetadata = MetadataStorage.instance.findMockMetadata(_target, propertyName)
20-
const metadata: MockPropertyMetadata = mergeConfig(preMetadata, {
21-
target: _target,
22-
propertyName,
23-
...config
24-
})
25-
26-
MetadataStorage.instance.addMockMetadata(metadata)
40+
if (!isClass) {
41+
const preMetadata = MetadataStorage.instance.findMockMetadata(_target, propertyName)
42+
const metadata: MockPropertyMetadata = mergeConfig(preMetadata, {
43+
target: _target,
44+
propertyName,
45+
...config
46+
})
47+
48+
MetadataStorage.instance.setMockMetadata(metadata)
49+
} else {
50+
MetadataStorage.instance.mergeAllPropertyMockMetadata(_target, config, (oldMeta, mergeMeta) => {
51+
const finalPartial =
52+
oldMeta.partial === undefined || oldMeta.partial === null || oldMeta.partial === 'auto'
53+
? mergeMeta?.partial ?? oldMeta.partial
54+
: oldMeta.partial
55+
56+
const finalAlwaysRandom =
57+
oldMeta.alwaysRandom === undefined || oldMeta.alwaysRandom === null
58+
? mergeMeta?.alwaysRandom ?? oldMeta.alwaysRandom
59+
: oldMeta.alwaysRandom
60+
61+
MetadataStorage.instance.updateMockMetadata(oldMeta.target, oldMeta.propertyName, {
62+
partial: finalPartial,
63+
alwaysRandom: finalAlwaysRandom
64+
})
65+
})
66+
}
2767
}
2868

29-
const createMergeConfigDecorator = (newConfig: T = <T>{}) => createConfigDecorator(mergeConfig(config, newConfig))
69+
const createMergeConfigDecorator = <IsClass extends boolean = false>(newConfig: T = <T>{}) =>
70+
createConfigDecorator<IsClass>(mergeConfig(config, newConfig))
3071

31-
const decoratorProto: MockPropertyDecoratorProps<T> = {
72+
const propertyDecoratorProto: MockPropertyDecoratorProps<T> = {
3273
config: (_config: T = <T>{}) => createMergeConfigDecorator(_config),
3374
isPartial: () => createMergeConfigDecorator(<T>{partial: 'partial'}),
3475
isInclude: () => createMergeConfigDecorator(<T>{partial: 'include'}),
3576
isExclude: () => createMergeConfigDecorator(<T>{partial: 'exclude'}),
3677
isAlwaysRandom: () => createMergeConfigDecorator(<T>{alwaysRandom: true}),
3778
isNotAlwaysRandom: () => createMergeConfigDecorator(<T>{alwaysRandom: false}),
38-
isArray: (arrayConfig?: ArrayConfig) => createMergeConfigDecorator(<T>{array: true, ...arrayConfig}),
79+
isArray: (arrayConfig?: ArrayConfig | number) =>
80+
createMergeConfigDecorator(<T>{
81+
array: true,
82+
...(typeof arrayConfig === 'number' ? {length: arrayConfig} : arrayConfig)
83+
}),
3984
isNotArray: () => createMergeConfigDecorator(<T>{array: false}),
4085
groups: (groups: string[]) => createMergeConfigDecorator(<T>{groups})
4186
}
4287

43-
Object.assign(decorator, decoratorProto)
88+
const classDecoratorProto: MockClassDecoratorProps<T> = {
89+
DefaultPartial: () => createMergeConfigDecorator<true>(<T>{partial: 'partial'}),
90+
DefaultInclude: () => createMergeConfigDecorator<true>(<T>{partial: 'include'}),
91+
DefaultExclude: () => createMergeConfigDecorator<true>(<T>{partial: 'exclude'}),
92+
DefaultAlwaysRandom: () => createMergeConfigDecorator<true>(<T>{alwaysRandom: true}),
93+
DefaultNotAlwaysRandom: () => createMergeConfigDecorator<true>(<T>{alwaysRandom: false})
94+
}
95+
96+
Object.assign(decorator, isClass ? classDecoratorProto : propertyDecoratorProto)
4497

45-
return decorator as MockPropertyDecorator<T>
98+
return decorator as IsClass extends true ? MockClassDecorator<T> : MockPropertyDecorator<T>
4699
}
47100

48101
return createConfigDecorator(config)
49102
}
103+
104+
const _decoratorProto = ConfigDecorator()
105+
export const Config = _decoratorProto.config
106+
export const IsPartial = _decoratorProto.isPartial
107+
export const IsInclude = _decoratorProto.isInclude
108+
export const IsExclude = _decoratorProto.isExclude
109+
export const IsAlwaysRandom = _decoratorProto.isAlwaysRandom
110+
export const IsNotAlwaysRandom = _decoratorProto.isNotAlwaysRandom
111+
export const IsArray = _decoratorProto.isArray
112+
export const IsNotArray = _decoratorProto.isNotArray
113+
export const Groups = _decoratorProto.groups
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import {BaseClass, MockPropertyDecorator, MockPropertyDecoratorConfig, MockPropertyMetadata} from '@/utils/types-helper'
2+
import {ConfigDecorator} from './config.decorator'
3+
4+
export const Entity = <T extends BaseClass>(getEntity: () => T): MockPropertyDecorator<MockPropertyDecoratorConfig> => {
5+
return ConfigDecorator<MockPropertyDecoratorConfig>({
6+
entityFn: getEntity
7+
} as MockPropertyMetadata)
8+
}

packages/class-mock/src/decorators/faker.decorator.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import {MockDecorator} from './mock.decorator'
77
export type FakeProp = typeof fakeProps[number]
88
export type MockFaker = Pick<Faker, FakeProp>
99
const createFakeProxy = <T extends keyof MockFaker, KeyFns extends keyof MockFaker[T]>(fakeKey: T) => {
10-
const mockFaker = faker as MockFaker
11-
1210
// fix Parameter params only allow function
1311
type MockFnParameters<T> = T extends (...args: infer P) => any ? P : never
1412

@@ -21,7 +19,7 @@ const createFakeProxy = <T extends keyof MockFaker, KeyFns extends keyof MockFak
2119

2220
return new Proxy(faker[fakeKey], {
2321
get(target, targetKey: string) {
24-
const mockFn = mockFaker[fakeKey][targetKey as KeyFns] as unknown as Fn
22+
const mockFn = target[targetKey as KeyFns] as unknown as Fn
2523
return (...params: any[]) => MockDecorator(mockFn, ...params)
2624
}
2725
}) as unknown as MockProxy
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export * from './config.decorator'
2+
export * from './mock.decorator'
3+
export * from './class.decorator'
4+
export * from './entity.decorator'
5+
export * from './faker.decorator'

packages/class-mock/src/index.ts

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,48 @@
1-
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
import 'reflect-metadata'
3-
import faker from '@faker-js/faker'
4-
import {Random, Name, Phone, Address} from './decorators/faker.decorator'
5-
import {createMock} from './utils/create-mock'
6-
7-
faker.setLocale('zh_CN')
8-
9-
class User {
10-
@Name.firstName()
11-
name!: string
12-
13-
@Random.number({min: 0, max: 100})
14-
age!: number
15-
}
16-
17-
class Student extends User {
18-
@Address.streetAddress().isArray({length: 3})
19-
address!: string[]
20-
21-
@Phone.phoneNumber('188########')
22-
tel!: number
23-
}
24-
25-
const mockStudent = createMock(Student)
26-
27-
console.log('mockStudent: ', mockStudent)
28-
29-
// 比如提供一个 webpack 插件或者 vite 插件,配置一下,axios 或 fetch 访问该 url 就可以自动响应 mock 数据了
30-
// const serverPluginConfig = [
31-
// {
32-
// url: '/api/users',
33-
// res: () => createMock(Student, {array: true})
34-
// },
35-
// {
36-
// url: '/api/user/:id',
37-
// res: () => createMock(Student, {array: false})
38-
// }
39-
// ]
1+
export {
2+
// custom decorator
3+
MockDecorator,
4+
// property config decorator
5+
Config,
6+
IsPartial,
7+
IsInclude,
8+
IsExclude,
9+
IsAlwaysRandom,
10+
IsNotAlwaysRandom,
11+
IsArray,
12+
IsNotArray,
13+
// entity decorator
14+
Entity,
15+
// faker decorators
16+
Mersenne,
17+
Random,
18+
Helpers,
19+
Datatype,
20+
Address,
21+
Animal,
22+
Commerce,
23+
Company,
24+
Database,
25+
Date,
26+
Finance,
27+
Git,
28+
Hacker,
29+
Image,
30+
Internet,
31+
Lorem,
32+
Music,
33+
Name,
34+
Phone,
35+
System,
36+
Time,
37+
Vehicle,
38+
Word,
39+
// class decorators
40+
DefaultPartial,
41+
DefaultInclude,
42+
DefaultExclude,
43+
DefaultAlwaysRandom,
44+
DefaultNotAlwaysRandom
45+
} from './decorators'
46+
export {createMock} from './utils/create-mock'
47+
export type {CreateMockOptions} from './utils/create-mock'
48+
export * from './utils/types-helper'

0 commit comments

Comments
 (0)