ivue 0.0.32 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc.json +14 -0
- package/.eslintrc.cjs +24 -0
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/compiler.xml +7 -0
- package/.idea/infinite-vue.iml +10 -0
- package/.idea/inspectionProfiles/Project_Default.xml +11 -0
- package/.idea/modules.xml +8 -0
- package/.idea/php.xml +20 -0
- package/.idea/vcs.xml +6 -0
- package/.vscode/extensions.json +3 -0
- package/LICENSE +19 -0
- package/README.md +31 -0
- package/demo/App.vue +72 -0
- package/demo/components/IVueTest.vue +298 -0
- package/demo/components/PreComponent.vue +13 -0
- package/demo/components/SubComponent2.vue +45 -0
- package/demo/components/TraitsComponent.vue +120 -0
- package/demo/components/useIVueTest.vue +136 -0
- package/demo/main.ts +21 -0
- package/demo/router/index.ts +14 -0
- package/demo/tests/IVueTests.ts +255 -0
- package/demo/tests/Mouse.ts +79 -0
- package/demo/tests/ParentIVueTests.ts +36 -0
- package/demo/tests/TApp.ts +15 -0
- package/demo/tests/TAuth.ts +23 -0
- package/demo/tests/TField.ts +84 -0
- package/demo/tests/TMessage.ts +52 -0
- package/demo/tests/TRouter.ts +18 -0
- package/demo/tests/TUser.ts +19 -0
- package/demo/tests/Test.ts +86 -0
- package/demo/tests/useField.ts +82 -0
- package/demo/tests/useIVueTests.ts +57 -0
- package/demo/vite.config.ts +13 -0
- package/dist/favicon.ico +0 -0
- package/dist/index.es.js +518 -0
- package/dist/index.umd.js +1 -0
- package/dist/src/behavior.d.ts +72 -0
- package/dist/src/config.d.ts +6 -0
- package/dist/src/index.d.ts +21 -0
- package/dist/src/ivue.d.ts +35 -0
- package/dist/src/kernel.d.ts +182 -0
- package/dist/src/traits/UseCapsule.d.ts +9 -0
- package/dist/src/traits/UseEmit.d.ts +3 -0
- package/dist/src/traits/UseInstance.d.ts +6 -0
- package/dist/src/traits/UseMounting.d.ts +11 -0
- package/dist/src/traits/UseRefs.d.ts +8 -0
- package/dist/src/traits/UseRoute.d.ts +3 -0
- package/dist/src/traits/UseRouter.d.ts +3 -0
- package/dist/src/traits/UseRouting.d.ts +6 -0
- package/dist/src/traits/UseTick.d.ts +4 -0
- package/dist/src/traits/UseVue.d.ts +13 -0
- package/dist/src/types/core.d.ts +46 -0
- package/dist/src/utils/dispose-garbage.d.ts +12 -0
- package/dist/src/utils/extend.d.ts +12 -0
- package/dist/src/utils/getters.d.ts +21 -0
- package/dist/src/utils/index.d.ts +7 -0
- package/dist/src/utils/is.d.ts +14 -0
- package/dist/src/utils/pre.d.ts +9 -0
- package/dist/src/utils/raw.d.ts +12 -0
- package/dist/src/utils/safe-class-name.d.ts +7 -0
- package/dist/src/utils/traits.d.ts +41 -0
- package/env.d.ts +1 -0
- package/index.html +13 -0
- package/package-lock.json +14305 -0
- package/package.json +89 -10
- package/public/favicon.ico +0 -0
- package/src/App/App.ts +27 -0
- package/src/App/Auth/Auth.ts +105 -0
- package/src/App/Auth/Auth.vue +98 -0
- package/src/App/Auth/Logout.vue +14 -0
- package/src/App/Auth/User.ts +6 -0
- package/src/App/BlackBox.ts +75 -0
- package/src/App/Config.ts +6 -0
- package/src/App/Generators/generators.ts +47 -0
- package/src/App/Home/Home.vue +16 -0
- package/src/App/Services/Http/FakeHttp.ts +8 -0
- package/src/App/Services/Http/Http.ts +51 -0
- package/src/App/Services/Message/Message.ts +49 -0
- package/src/App/Services/Message/Message.vue +15 -0
- package/src/App/Services/Navigation/Navigation.ts +83 -0
- package/src/App/Services/Navigation/Navigation.vue +59 -0
- package/src/App/Services/Routing/FakeRouter.ts +7 -0
- package/src/App/Services/Routing/NotFoundRoute.vue +13 -0
- package/src/App/Services/Routing/Router.ts +262 -0
- package/src/App/Services/Routing/Routes.ts +49 -0
- package/src/App/Stubs/SingleBookResultStub.ts +13 -0
- package/src/App/Traits/UseApp.ts +6 -0
- package/src/App/Traits/UseBlackline.ts +0 -0
- package/src/App/Traits/UseQuasar.ts +8 -0
- package/src/behavior.ts +269 -0
- package/src/config.ts +15 -0
- package/src/dev/dev-utils.ts +78 -0
- package/src/index.ts +83 -0
- package/src/ivue.ts +296 -0
- package/src/kernel.ts +810 -0
- package/src/tests/auth.spec.ts +48 -0
- package/src/traits/UseCapsule.ts +28 -0
- package/src/traits/UseEmit.ts +3 -0
- package/src/traits/UseInstance.ts +17 -0
- package/src/traits/UseMounting.ts +23 -0
- package/src/traits/UseRefs.ts +17 -0
- package/src/traits/UseRoute.ts +6 -0
- package/src/traits/UseRouter.ts +5 -0
- package/src/traits/UseRouting.ts +7 -0
- package/src/traits/UseTick.ts +5 -0
- package/src/traits/UseVue.ts +38 -0
- package/src/types/core.ts +64 -0
- package/src/utils/dispose-garbage.ts +34 -0
- package/src/utils/extend.ts +85 -0
- package/src/utils/getters.ts +62 -0
- package/src/utils/index.ts +16 -0
- package/src/utils/is.ts +22 -0
- package/src/utils/pre.ts +29 -0
- package/src/utils/raw.ts +19 -0
- package/src/utils/safe-class-name.ts +15 -0
- package/src/utils/traits.ts +121 -0
- package/tsconfig.app.json +47 -0
- package/tsconfig.json +14 -0
- package/tsconfig.node.json +14 -0
- package/tsconfig.vitest.json +9 -0
- package/vite.config.ts +55 -0
- package/vitest.config.ts +18 -0
- package/dist/index.js +0 -218
- package/docs/index.html +0 -195
- package/docs/index.json +0 -3
- package/docs/input.html +0 -41
- package/docs/items.html +0 -42
- package/gulpfile.js +0 -5
- package/src/components/checkbox.js +0 -47
- package/src/components/data.js +0 -6
- package/src/components/file.js +0 -40
- package/src/components/form.js +0 -7
- package/src/components/index.js +0 -15
- package/src/components/input.js +0 -80
- package/src/components/items.js +0 -65
- package/src/components/modal.js +0 -42
- package/src/components/paginate-old.js +0 -88
- package/src/components/paginate.js +0 -35
- package/src/components/select-search.js +0 -152
- package/src/components/select.js +0 -62
- package/src/components/submit.js +0 -10
- package/src/components/textarea.js +0 -72
- package/src/components/upload.js +0 -49
- package/src/directives/href.js +0 -10
- package/src/directives/index.js +0 -1
- package/src/filters/date.js +0 -3
- package/src/filters/datetime.js +0 -3
- package/src/filters/index.js +0 -4
- package/src/filters/json.js +0 -3
- package/src/filters/money.js +0 -3
- package/src/index.js +0 -4
- package/src/mixins/data.js +0 -13
- package/src/mixins/form.js +0 -85
- package/src/mixins/resource.js +0 -31
- package/src/services/client.js +0 -32
- package/src/services/flash.js +0 -17
- package/src/services/index.js +0 -2
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, it, beforeEach, expect, vi } from 'vitest'
|
|
2
|
+
import { BlackBox } from '../App/BlackBox'
|
|
3
|
+
import type { App } from '@/App/App';
|
|
4
|
+
|
|
5
|
+
let box: BlackBox
|
|
6
|
+
let app: App
|
|
7
|
+
|
|
8
|
+
function wait () {
|
|
9
|
+
return setTimeout(() => {})
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('--- auth test ----', () => {
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
box = new BlackBox().init()
|
|
16
|
+
app = box.app
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('root route redirects to login', async () => {
|
|
20
|
+
await app.router.push({ name: 'root' })
|
|
21
|
+
expect(app.router.active.name).toBe('login')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('successful login redirects to homepage', async () => {
|
|
25
|
+
await app.router.push({ name: 'root' })
|
|
26
|
+
|
|
27
|
+
app.auth.email = 'ekalashnikov@gmail.com'
|
|
28
|
+
app.auth.password = 'test1234'
|
|
29
|
+
|
|
30
|
+
const spy = vi.spyOn(app.router, 'push')
|
|
31
|
+
|
|
32
|
+
await app.auth.login()
|
|
33
|
+
|
|
34
|
+
expect(spy).toHaveBeenCalledTimes(1)
|
|
35
|
+
expect(spy).toHaveBeenCalledWith({ name: 'home' })
|
|
36
|
+
|
|
37
|
+
expect(app.message.appMessages).toEqual(['User logged in'])
|
|
38
|
+
|
|
39
|
+
expect(app.user.email).toBe('ekalashnikov@gmail.com')
|
|
40
|
+
expect(app.user.token).toBe('1234ekalashnikov@gmail.com')
|
|
41
|
+
|
|
42
|
+
await app.router.isLoaded()
|
|
43
|
+
|
|
44
|
+
expect(app.router.currentRoute.name).toBe('home')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
})
|
|
48
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { get, make, use, init } from '../kernel';
|
|
2
|
+
import type { AnyClass } from '../types/core';
|
|
3
|
+
import { markRaw } from 'vue';
|
|
4
|
+
|
|
5
|
+
export class UseCapsule {
|
|
6
|
+
|
|
7
|
+
$!: InstanceType<this['capsule']>
|
|
8
|
+
|
|
9
|
+
capsule: AnyClass | any
|
|
10
|
+
|
|
11
|
+
useCapsule (args: any = []) {
|
|
12
|
+
this.$ = use(this.capsule, this, ...args)
|
|
13
|
+
markRaw(this.$)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
initCapsule (args: any = []) {
|
|
17
|
+
this.$ = init(this.capsule, this, ...args)
|
|
18
|
+
markRaw(this.$)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getCapsule (args: any = []) {
|
|
22
|
+
this.$ = get(this.capsule, this, ...args)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
makeCapsule (args: any = []) {
|
|
26
|
+
this.$ = make(this.capsule, this, ...args)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Class } from '../types/core'
|
|
2
|
+
import { IVUE } from '../behavior';
|
|
3
|
+
import { extend } from '../utils/extend'
|
|
4
|
+
import { getCurrentInstance } from 'vue';
|
|
5
|
+
|
|
6
|
+
export function useInstance (mainClass: Class, self: UseInstance | any) {
|
|
7
|
+
mainClass.behavior = extend(mainClass.behavior ?? {}, { $instance: IVUE.OFF })
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class UseInstance {
|
|
11
|
+
get $instance () {
|
|
12
|
+
return getCurrentInstance()
|
|
13
|
+
}
|
|
14
|
+
constructor (mainClass: Class) {
|
|
15
|
+
useInstance(mainClass, this)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Class } from '../types/core';
|
|
2
|
+
import { after } from '../behavior'
|
|
3
|
+
import { onMounted, onUnmounted, onBeforeMount, onBeforeUnmount } from 'vue'
|
|
4
|
+
|
|
5
|
+
export function useMounting (mainClass: Class, obj: UseMounting | any) {
|
|
6
|
+
after(mainClass, ({ self }) => {
|
|
7
|
+
if ('beforeMount' in self) onBeforeMount(() => self.beforeMount())
|
|
8
|
+
if ('mounted' in self) onMounted(() => self.mounted())
|
|
9
|
+
if ('beforeUnmount' in self) onBeforeUnmount(() => self.beforeUnmount())
|
|
10
|
+
if ('unmounted' in self) onUnmounted(() => self.unmounted())
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Make a class useMounting to a Vue component */
|
|
15
|
+
export class UseMounting {
|
|
16
|
+
constructor (mainClass: Class) {
|
|
17
|
+
useMounting(mainClass, this)
|
|
18
|
+
}
|
|
19
|
+
beforeMount?: ReturnType<typeof onBeforeMount> = function () {}
|
|
20
|
+
mounted?: ReturnType<typeof onMounted> = function () {}
|
|
21
|
+
beforeUnmount?: ReturnType<typeof onBeforeUnmount> = function () {}
|
|
22
|
+
unmounted?: ReturnType<typeof onUnmounted> = function () {}
|
|
23
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Class } from '../types/core';
|
|
2
|
+
import { IVUE } from '../behavior';
|
|
3
|
+
import { extend } from '../utils/extend'
|
|
4
|
+
import { getCurrentInstance } from 'vue';
|
|
5
|
+
|
|
6
|
+
export function useRefs (mainClass: Class, obj: any) {
|
|
7
|
+
mainClass.behavior = extend(mainClass.behavior ?? {}, { $refs: IVUE.OFF })
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class UseRefs {
|
|
11
|
+
get $refs () {
|
|
12
|
+
return getCurrentInstance()?.refs;
|
|
13
|
+
}
|
|
14
|
+
constructor (mainClass: Class) {
|
|
15
|
+
useRefs(mainClass, this)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Class } from '../types/core'
|
|
2
|
+
import { Traits } from '../utils/traits';
|
|
3
|
+
import { useMounting, UseMounting } from './UseMounting';
|
|
4
|
+
import { UseRouting } from './UseRouting';
|
|
5
|
+
import { useRefs, UseRefs } from './UseRefs';
|
|
6
|
+
import { UseEmit } from './UseEmit';
|
|
7
|
+
import { UseTick } from './UseTick';
|
|
8
|
+
import { UseInstance, useInstance } from './UseInstance';
|
|
9
|
+
|
|
10
|
+
export function useVue (mainClass: Class, obj: UseVue | any) {
|
|
11
|
+
useInstance(mainClass, obj)
|
|
12
|
+
useRefs(mainClass, obj)
|
|
13
|
+
useMounting(mainClass, obj)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class UseVue {
|
|
17
|
+
constructor (mainClass: Class) {
|
|
18
|
+
useVue(mainClass, this)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
Traits(UseVue, [
|
|
23
|
+
UseRouting,
|
|
24
|
+
UseMounting,
|
|
25
|
+
UseEmit,
|
|
26
|
+
UseTick,
|
|
27
|
+
UseRefs,
|
|
28
|
+
UseInstance
|
|
29
|
+
])
|
|
30
|
+
|
|
31
|
+
export interface UseVue
|
|
32
|
+
extends
|
|
33
|
+
UseRouting,
|
|
34
|
+
UseMounting,
|
|
35
|
+
UseEmit,
|
|
36
|
+
UseTick,
|
|
37
|
+
UseRefs,
|
|
38
|
+
UseInstance {}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Mapping } from '../kernel'
|
|
2
|
+
import type { ComputedRef } from 'vue';
|
|
3
|
+
|
|
4
|
+
export type AnyClass = abstract new (...args: any) => any;
|
|
5
|
+
|
|
6
|
+
export type Class = {
|
|
7
|
+
behavior?: any;
|
|
8
|
+
new(...args: any[]): any;
|
|
9
|
+
[x: string | symbol]: any
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type IVue<T extends AnyClass> = InstanceType<T> & IVueToRefs<T>
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @see ivueTransform
|
|
16
|
+
*/
|
|
17
|
+
export interface IVueToRefs<T extends AnyClass> {
|
|
18
|
+
toRefs: (...args: any) => InstanceType<T>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface IVueToRefsObj<T extends object> {
|
|
22
|
+
toRefs: (...args: any) => T
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type MappingScope = 'singleton' | 'transient'
|
|
26
|
+
|
|
27
|
+
export type MappingType = 'generic' | 'ivue' | any
|
|
28
|
+
|
|
29
|
+
export type InferredArgs<T> = T extends { new(...args: infer P): any } ? P : never[]
|
|
30
|
+
|
|
31
|
+
export type Getters = Map<string | symbol | number, PropertyDescriptor>
|
|
32
|
+
|
|
33
|
+
export interface Intercept {
|
|
34
|
+
return: any,
|
|
35
|
+
mapping: Mapping,
|
|
36
|
+
name?: string,
|
|
37
|
+
self: any,
|
|
38
|
+
args: any
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type InterceptOptions = {
|
|
42
|
+
top: boolean,
|
|
43
|
+
scoped: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type InterceptFn = (intercept: Intercept, self: Object, prop: string) => void | true
|
|
47
|
+
|
|
48
|
+
export type InterceptsMap = {
|
|
49
|
+
before: Map<Function | Class, InterceptFn[]>,
|
|
50
|
+
after: Map<Function | Class, InterceptFn[]>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type InterceptAttachTo = Function | [Class, Function]
|
|
54
|
+
|
|
55
|
+
export type InterceptsFns = InterceptFn[] | undefined | any
|
|
56
|
+
|
|
57
|
+
export type Null<T> = T | null
|
|
58
|
+
|
|
59
|
+
export type AnyObj = Record<string | number | symbol, any>
|
|
60
|
+
|
|
61
|
+
export type Params<T extends (...args: any) => any> = Parameters<T>
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
export type Computeds = Record<string | symbol | number, ComputedRef>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { toRaw } from "vue"
|
|
2
|
+
import type { AnyClass, Getters, IVue } from ".."
|
|
3
|
+
const propertiesMap = new WeakMap()
|
|
4
|
+
/**
|
|
5
|
+
* Advanced garbage disposal for complete memory control.
|
|
6
|
+
* Disposes an internal object that is under the vue reactive proxy.
|
|
7
|
+
* To expose the raw property, toRaw(vue) method is used
|
|
8
|
+
* to retrieve the original object.
|
|
9
|
+
*
|
|
10
|
+
* @param vue ivue instance
|
|
11
|
+
* @param getters @see Getters
|
|
12
|
+
* @param name string
|
|
13
|
+
*/
|
|
14
|
+
export const disposeGarbage = <T extends AnyClass> (vue: IVue<T> | any) => {
|
|
15
|
+
// Deep garbage collection, gives really amazing performance in Opera as well as Google Chrome.
|
|
16
|
+
// Opera seems to perform better though with cleaning up the garbage more effeciently and more.
|
|
17
|
+
|
|
18
|
+
let instance = toRaw(vue)
|
|
19
|
+
let props: any = Object.getOwnPropertyNames(instance)
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < props.length; i++) {
|
|
22
|
+
delete instance[props[i]]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Faster but less thorough garbage collection:
|
|
26
|
+
// Will remove only enumerable properties.
|
|
27
|
+
// for (prop in vue) {
|
|
28
|
+
// delete vue[prop]
|
|
29
|
+
// }
|
|
30
|
+
|
|
31
|
+
// Unset roots for gc engine to collect and dispose
|
|
32
|
+
instance = null
|
|
33
|
+
props = null
|
|
34
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { isObject, isPromise } from "./is";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Advanced deep merge of two or more objects.
|
|
5
|
+
* Merges sources into target iterating from left to right.
|
|
6
|
+
* Supports circular references and promises.
|
|
7
|
+
* Merges and modifies the target object.
|
|
8
|
+
* Only simple objects can extend the target, yet the
|
|
9
|
+
* target can be anything, even a class object.
|
|
10
|
+
*
|
|
11
|
+
* @param target object
|
|
12
|
+
* @param sources array
|
|
13
|
+
*/
|
|
14
|
+
export function extend (target: any, ...sources: any[]) {
|
|
15
|
+
let parents: any = new WeakMap() // parents object to detect circular references
|
|
16
|
+
const result = _extend(target, [...sources], parents);
|
|
17
|
+
// parents = undefined // flush the parents map upon completion
|
|
18
|
+
return result
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Deep merge two objects inner function.
|
|
23
|
+
*
|
|
24
|
+
* @param target Target - object to merge into.
|
|
25
|
+
* @param sources Sources - object to merge from.
|
|
26
|
+
* @param parents Parents - for circular reference checks.
|
|
27
|
+
* @return {*}
|
|
28
|
+
*
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
function _extend<K, V> (target: any, sources: any[], parents: Map<K, V> | null | any = null) {
|
|
32
|
+
|
|
33
|
+
if (!sources.length) return target;
|
|
34
|
+
// save target object in Map as a key
|
|
35
|
+
parents.set(target, true)
|
|
36
|
+
|
|
37
|
+
const source = sources.shift();
|
|
38
|
+
|
|
39
|
+
if (isObject(target) && isObject(source)) {
|
|
40
|
+
|
|
41
|
+
for (const key in source) {
|
|
42
|
+
if (isObject(source[key]) && source[key].constructor.name === 'Object') {
|
|
43
|
+
// detect circular references using Map parents object
|
|
44
|
+
if (parents.has(source[key])) {
|
|
45
|
+
// console.log('detected circular reference key:', key,
|
|
46
|
+
// 'value:', source[key],
|
|
47
|
+
// 'parents:', [...parents])
|
|
48
|
+
target[key] = source[key]
|
|
49
|
+
} else {
|
|
50
|
+
if (isPromise(source[key])) {
|
|
51
|
+
// promise objects should not be assigned
|
|
52
|
+
// via _extend
|
|
53
|
+
target[key] = source[key]
|
|
54
|
+
} else {
|
|
55
|
+
// the following lines
|
|
56
|
+
// integrate Proxy support
|
|
57
|
+
// if the target key does not exist
|
|
58
|
+
if (typeof target[key] === 'undefined') {
|
|
59
|
+
// assign directly the proxy or any other object
|
|
60
|
+
target[key] = source[key]
|
|
61
|
+
} else {
|
|
62
|
+
// if target[key] is not an object
|
|
63
|
+
// convert it to an object to be able to extend
|
|
64
|
+
if (!isObject(target[key])) {
|
|
65
|
+
target[key] = source[key]
|
|
66
|
+
} else {
|
|
67
|
+
// Only simple objects can extend the target
|
|
68
|
+
if (target[key].constructor.name !== 'Object') {
|
|
69
|
+
target[key] = source[key]
|
|
70
|
+
} else {
|
|
71
|
+
_extend(target[key], [source[key]], parents)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
} else {
|
|
79
|
+
target[key] = source[key]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return _extend(target, sources, parents);
|
|
85
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Getters } from '../types/core'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Store all getters for each class prototype processed by getPrototypeGetters.
|
|
5
|
+
* @see getPrototypeGetters
|
|
6
|
+
*/
|
|
7
|
+
export const gettersMap = new Map()
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get getters of a class prototype as a map.
|
|
11
|
+
*
|
|
12
|
+
* @param proto
|
|
13
|
+
* @return Getters
|
|
14
|
+
*/
|
|
15
|
+
export function getPrototypeGetters (proto: object | null) {
|
|
16
|
+
|
|
17
|
+
if (gettersMap.has(proto)) return gettersMap.get(proto)
|
|
18
|
+
|
|
19
|
+
const initialProto = proto
|
|
20
|
+
|
|
21
|
+
const props: any[] = []
|
|
22
|
+
|
|
23
|
+
while (proto && proto.constructor.name !== 'Object') {
|
|
24
|
+
props.push.apply(props, Object.entries(
|
|
25
|
+
Object.getOwnPropertyDescriptors(proto)
|
|
26
|
+
))
|
|
27
|
+
proto = Object.getPrototypeOf(proto.constructor.prototype)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const getters: Getters = new Map()
|
|
31
|
+
|
|
32
|
+
for (let i = 0; i < props.length; i++) {
|
|
33
|
+
if (props[i][0] !== '__proto__' && typeof props[i][1].get === 'function') {
|
|
34
|
+
getters.set(props[i][0], props[i][1])
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Save getters in the getters map.
|
|
39
|
+
*/
|
|
40
|
+
gettersMap.set(initialProto, getters)
|
|
41
|
+
|
|
42
|
+
return getters
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get getters of a class instance and the total number of them.
|
|
47
|
+
* @param instance Object instance
|
|
48
|
+
* @returns Getter descriptors
|
|
49
|
+
*/
|
|
50
|
+
export function getInstanceGetters (instance: { [x: string]: any; }) {
|
|
51
|
+
|
|
52
|
+
const getters: Getters = new Map()
|
|
53
|
+
const props = Object.entries(Object.getOwnPropertyDescriptors(instance))
|
|
54
|
+
|
|
55
|
+
for (let i = 0; i < props.length; i++) {
|
|
56
|
+
if (typeof props[i][1].get === 'function' && props[i][0] !== '__proto__') {
|
|
57
|
+
getters.set(props[i][0], props[i][1])
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return getters
|
|
62
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { raw, unraw } from './raw'
|
|
2
|
+
import { extend } from './extend'
|
|
3
|
+
import { Traits } from './traits'
|
|
4
|
+
import { getPrototypeGetters, getInstanceGetters } from './getters'
|
|
5
|
+
import { safeClassName } from './safe-class-name';
|
|
6
|
+
import { pre } from './pre';
|
|
7
|
+
export {
|
|
8
|
+
raw,
|
|
9
|
+
unraw,
|
|
10
|
+
extend,
|
|
11
|
+
Traits,
|
|
12
|
+
getPrototypeGetters,
|
|
13
|
+
getInstanceGetters,
|
|
14
|
+
safeClassName,
|
|
15
|
+
pre
|
|
16
|
+
}
|
package/src/utils/is.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Determine if the value is a true Object.
|
|
3
|
+
*
|
|
4
|
+
* @param v any value
|
|
5
|
+
* @returns boolean
|
|
6
|
+
*/
|
|
7
|
+
export function isObject (v: any) {
|
|
8
|
+
return (v && typeof v === 'object' && !Array.isArray(v));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Determine if the value is a Promise.
|
|
13
|
+
*
|
|
14
|
+
* @param v any value
|
|
15
|
+
* @returns boolean
|
|
16
|
+
*/
|
|
17
|
+
export function isPromise (v: any) {
|
|
18
|
+
return v !== null &&
|
|
19
|
+
typeof v === 'object' &&
|
|
20
|
+
typeof v.then === 'function' &&
|
|
21
|
+
typeof v.catch === 'function';
|
|
22
|
+
}
|
package/src/utils/pre.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pretty print an object that can contain circular references.
|
|
3
|
+
*
|
|
4
|
+
* @param v object to print
|
|
5
|
+
* @param spaces number of spaces for tabs
|
|
6
|
+
* @returns pretty string
|
|
7
|
+
* @source https://stackoverflow.com/a/48254637
|
|
8
|
+
*/
|
|
9
|
+
export function pre (v: any, spaces = 2) {
|
|
10
|
+
const cache = new Set();
|
|
11
|
+
return JSON.stringify(v, function (key, value) {
|
|
12
|
+
if (typeof value === 'object' && value !== null) {
|
|
13
|
+
if (cache.has(value)) {
|
|
14
|
+
// Circular reference found
|
|
15
|
+
try {
|
|
16
|
+
// If this value does not reference a parent it can be deduped
|
|
17
|
+
return JSON.parse(JSON.stringify(value))
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
// discard key if value cannot be deduped
|
|
21
|
+
return '[**circular**]';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Store value in our set
|
|
25
|
+
cache.add(value)
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}, spaces)
|
|
29
|
+
}
|
package/src/utils/raw.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { markRaw } from 'vue';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convert object back to Vue reactive.
|
|
5
|
+
*
|
|
6
|
+
* @param obj
|
|
7
|
+
*/
|
|
8
|
+
export function unraw (obj: any) {
|
|
9
|
+
delete obj.__v_skip
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Alias for Vue markRaw() function.
|
|
14
|
+
*
|
|
15
|
+
* @param obj
|
|
16
|
+
*/
|
|
17
|
+
export function raw (obj: any) {
|
|
18
|
+
return markRaw(obj)
|
|
19
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert class name, symbol to string.
|
|
3
|
+
*
|
|
4
|
+
* @param className
|
|
5
|
+
* @returns
|
|
6
|
+
*/
|
|
7
|
+
export function safeClassName(className: any) {
|
|
8
|
+
let name = ''
|
|
9
|
+
try {
|
|
10
|
+
name = className?.name || String(className)
|
|
11
|
+
} catch (e) {
|
|
12
|
+
name = 'Unknown[failed to convert to string]'
|
|
13
|
+
}
|
|
14
|
+
return name
|
|
15
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { Class } from '../types/core'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Traits architecture that supports methods, properties, getters and setters.
|
|
5
|
+
*
|
|
6
|
+
* Traits override each other, while traits can set new props onto
|
|
7
|
+
* the main prototype, they cannot override the main prototypes'
|
|
8
|
+
* props if the main prototype had them already initially.
|
|
9
|
+
*
|
|
10
|
+
* Traits override each other, left most traits having higher priority, matching
|
|
11
|
+
* TypeScript's interface `interface Class extends Trait1, Trait2, Trait3 {}`
|
|
12
|
+
* architecture, enabling us to have full TypeScript & IDE support:
|
|
13
|
+
*
|
|
14
|
+
* Example:
|
|
15
|
+
* ```ts
|
|
16
|
+
* class MainClass {}
|
|
17
|
+
*
|
|
18
|
+
* class Trait1 {
|
|
19
|
+
* get prop () { return 3 }
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* class Trait2 {
|
|
23
|
+
* get prop () { return 2 }
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* class Trait3 {
|
|
27
|
+
* get prop () { return 3 }
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* Traits(MainClass, [Trait1, Trait2, Trait3])
|
|
31
|
+
* export interface MainClass extends Trait1, Trait2, Trait3 {}
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Trait1 props will override Traits2 & Traits3 properties,
|
|
35
|
+
* but will not override MainClass properties if set.
|
|
36
|
+
*
|
|
37
|
+
* Inspired by TypeScript docs: https://www.typescriptlang.org/docs/handbook/mixins.html#alternative-pattern
|
|
38
|
+
*
|
|
39
|
+
* @param main
|
|
40
|
+
* @param traits
|
|
41
|
+
*/
|
|
42
|
+
export function Traits (main: Class, traits: Class[]): void {
|
|
43
|
+
|
|
44
|
+
const initialProtoSetterOnly: Record<string | number | symbol, Function> = {}
|
|
45
|
+
|
|
46
|
+
const initialProto = Object.getOwnPropertyNames(main.prototype)
|
|
47
|
+
.reduce((acc: any, prop: string) => {
|
|
48
|
+
const descriptor = Object.getOwnPropertyDescriptor(main.prototype, prop)
|
|
49
|
+
let skip = false
|
|
50
|
+
// skip setters without getters
|
|
51
|
+
if (descriptor?.set && !descriptor?.get) {
|
|
52
|
+
// collect setter only descriptors
|
|
53
|
+
initialProtoSetterOnly[prop] = descriptor?.set
|
|
54
|
+
skip = true
|
|
55
|
+
}
|
|
56
|
+
if (!skip) acc[prop] = true
|
|
57
|
+
return acc
|
|
58
|
+
}, {})
|
|
59
|
+
|
|
60
|
+
const notInInitialProto: Record<string, boolean> = {}
|
|
61
|
+
|
|
62
|
+
traits.reverse().forEach(trait => {
|
|
63
|
+
|
|
64
|
+
// Init the traits constructor to be able to perform
|
|
65
|
+
// transformations on the main class beyond
|
|
66
|
+
// just property & method inheritance.
|
|
67
|
+
const obj = new trait(main)
|
|
68
|
+
|
|
69
|
+
// Loop initialized properties, otherwise they are undefined
|
|
70
|
+
for (const prop in obj) {
|
|
71
|
+
|
|
72
|
+
if (!(prop in initialProto)) notInInitialProto[prop] = true
|
|
73
|
+
|
|
74
|
+
// Did prop exist on the initial state of main prototype?
|
|
75
|
+
// If it did, we do not want to override it
|
|
76
|
+
if (prop in notInInitialProto) {
|
|
77
|
+
Object.defineProperty(
|
|
78
|
+
main.prototype,
|
|
79
|
+
prop,
|
|
80
|
+
{
|
|
81
|
+
value: obj[prop],
|
|
82
|
+
writable: true
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Loop through all properties to get the methods
|
|
89
|
+
Object.getOwnPropertyNames(trait.prototype).forEach(prop => {
|
|
90
|
+
if (prop !== 'constructor') {
|
|
91
|
+
|
|
92
|
+
if (!(prop in initialProto)) notInInitialProto[prop] = true
|
|
93
|
+
|
|
94
|
+
// Did prop exist on the initial state of main prototype?
|
|
95
|
+
// If it did, we do not want to override it
|
|
96
|
+
if (prop in notInInitialProto) {
|
|
97
|
+
const descriptor = Object.getOwnPropertyDescriptor(trait.prototype, prop) || Object.create(null)
|
|
98
|
+
|
|
99
|
+
// This is to ensure that setters from other traits are passed down
|
|
100
|
+
// but only if the initialProto has no setter set
|
|
101
|
+
if (descriptor?.set && !(prop in initialProtoSetterOnly)) {
|
|
102
|
+
initialProtoSetterOnly[prop] = descriptor?.set
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// This is to ensure the initialProto with set only does not get overriden
|
|
106
|
+
// with traits that do not have a setter, but only have a getter
|
|
107
|
+
if (descriptor?.get && !descriptor?.set && prop in initialProtoSetterOnly) {
|
|
108
|
+
descriptor.set = initialProtoSetterOnly[prop]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
Object.defineProperty(
|
|
112
|
+
main.prototype,
|
|
113
|
+
prop,
|
|
114
|
+
descriptor
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
}
|