Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

newStore and Submodule #258

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9d8b83b
add vuexstore
clouds56 Apr 24, 2020
39f5329
fix namespace resolve
clouds56 Apr 24, 2020
e4aa731
fix for state path
clouds56 Apr 24, 2020
703164b
fix tests
clouds56 Apr 24, 2020
2dd8503
remove unused staticGenerator
clouds56 Apr 24, 2020
42a85c7
fix root
clouds56 Apr 24, 2020
01cb8d2
use $context and $paths
clouds56 Apr 24, 2020
9e2ffbf
use VuexModule.create instead of constructor
clouds56 Apr 25, 2020
2e6b947
add static to Module
clouds56 Apr 25, 2020
118300c
use new VuexModule
clouds56 Apr 25, 2020
16845ef
add staticModuleGenerators
clouds56 Apr 26, 2020
0ac8ac4
add newStore
clouds56 Apr 26, 2020
7a9d921
add support for actions
clouds56 Apr 26, 2020
01131cb
add submodule
clouds56 Apr 26, 2020
e80cc3e
fix register dynamic and add tests
clouds56 Apr 26, 2020
55f9848
some more tests
clouds56 Apr 26, 2020
926be9a
add test to Context
clouds56 Apr 26, 2020
e2101d1
fix for nested submodule
clouds56 Apr 26, 2020
1a5f4e0
fix factory
clouds56 Apr 26, 2020
2fb565d
some minor fixes
clouds56 Apr 26, 2020
58f0454
add context test
clouds56 Apr 26, 2020
896101e
export VueStore
clouds56 Apr 26, 2020
004c350
fix some test
clouds56 May 7, 2020
5880eba
remove getModule
clouds56 May 10, 2020
accc53b
add install
clouds56 May 10, 2020
b143efe
inplace replacement Store
clouds56 May 10, 2020
c79da4e
fix registerDynamicModule
clouds56 May 10, 2020
01b1f16
make namespace default to true
clouds56 May 10, 2020
1c819c8
fix action and remove unnecessay vue in test
clouds56 May 11, 2020
de885ef
fix test
clouds56 May 11, 2020
b3bf675
make stateFactory default to false
clouds56 May 10, 2020
65b5a38
fix test
clouds56 May 11, 2020
2f562d5
Merge branch 'master' into store
clouds56 May 11, 2020
e9eb4b4
Update action.ts
clouds56 May 11, 2020
b38208b
fix test in global_config
clouds56 May 11, 2020
27c920a
make dynamic module support nested path
clouds56 May 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 30 additions & 33 deletions src/action.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Action as Act, ActionContext, Module as Mod, Payload } from 'vuex'
import { getModule, VuexModule } from './vuexmodule'
import { addPropertiesToObject, getModuleName } from './helpers'
import { addPropertiesToObject } from './helpers'
import { config } from './config'

/**
Expand All @@ -19,45 +18,43 @@ function actionDecoratorFactory<T>(params?: ActionDecoratorParams): MethodDecora
module.actions = Object.assign({}, module.actions)
}
const actionFunction: Function = descriptor.value
const staticKey = '$statics/' + String(key)
const action: Act<typeof target, any> = async function(
context: ActionContext<typeof target, any>,
payload: Payload
) {
try {
let actionPayload = null
let actionPayload

if ((module as any)._genStatic) {
const moduleName = getModuleName(module)
const moduleAccessor = context.rootGetters[moduleName]
? context.rootGetters[moduleName]
: getModule(module as typeof VuexModule)
moduleAccessor.context = context
actionPayload = await actionFunction.call(moduleAccessor, payload)
} else {
const thisObj = { context }
addPropertiesToObject(thisObj, context.state)
addPropertiesToObject(thisObj, context.getters)
if (context.getters[staticKey]) {
const moduleAccessor = context.getters[staticKey]
moduleAccessor.context = context
actionPayload = await actionFunction.call(moduleAccessor, payload)
} else {
const thisObj = { context }
addPropertiesToObject(thisObj, context.state)
addPropertiesToObject(thisObj, context.getters)
try {
actionPayload = await actionFunction.call(thisObj, payload)
} catch (e) {
throw rawError
? e
: new Error(
'ERR_ACTION_ACCESS_UNDEFINED: Are you trying to access ' +
'this.someMutation() or this.someGetter inside an @Action? \n' +
'That works only in dynamic modules. \n' +
'If not dynamic use this.context.commit("mutationName", payload) ' +
'and this.context.getters["getterName"]' +
'\n' +
new Error(`Could not perform action ${key.toString()}`).stack +
'\n' +
e.stack
)
}
if (commit) {
context.commit(commit, actionPayload)
}
return actionPayload
} catch (e) {
throw rawError
? e
: new Error(
'ERR_ACTION_ACCESS_UNDEFINED: Are you trying to access ' +
'this.someMutation() or this.someGetter inside an @Action? \n' +
'That works only in dynamic modules. \n' +
'If not dynamic use this.context.commit("mutationName", payload) ' +
'and this.context.getters["getterName"]' +
'\n' +
new Error(`Could not perform action ${key.toString()}`).stack +
'\n' +
e.stack
)
}
if (commit) {
context.commit(commit, actionPayload)
}
return actionPayload
}
module.actions![key as string] = root ? { root, handler: action } : action
}
Expand Down
57 changes: 51 additions & 6 deletions src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { VueConstructor } from 'vue'
import Vuex from 'vuex'
import { ModuleMap } from './vuexmodule'

/**
* Takes the properties on object from parameter source and adds them to the object
* parameter target
Expand All @@ -12,15 +16,56 @@
}
}

/**
* Returns a path based name of the module to be used for store access
* @param path
*/
export function getStaticName(path: string[]): string {
if (path.length === 0) {
return '$statics'
}
return '$statics.' + path.join('.')
}

/**
* Returns a namespaced name of the module to be used as a store getter
* @param module
* @param path
* @param namespaced
*/
export function getModuleName(module: any): string {
if (!module._vmdModuleName) {
throw new Error(`ERR_GET_MODULE_NAME : Could not get module accessor.
Make sure your module has name, we can't make accessors for unnamed modules
i.e. @Module({ name: 'something' })`)
export function getModuleNamespace(module: ModuleMap, path: string[], namespaced: boolean): string {
let namespace = ''
if (path.length === 0) {
return namespace

Check warning on line 39 in src/helpers.ts

View check run for this annotation

Codecov / codecov/patch

src/helpers.ts#L39

Added line #L39 was not covered by tests
}
for (const key of path.slice(0, -1)) {
if (!module.modules || !module.modules[key]) {
throw new Error(`ERR_DYNAMIC_MODULE_NOT_EXISTS : Could not create module.
Make sure your path to your dynamic submodule exists
i.e. @Module({ name: "path.to.exits.name", store, dynamic: true })`)
}
module = module.modules[key]
if (module.namespaced) {
namespace = namespace + key + '/'
}
}
if (namespaced) {
return namespace + path[path.length - 1]
}
return namespace.slice(0, -1)
}

export function getNamespacedKey(namespace: string | null | undefined, key: string) {
return namespace ? `${namespace}/${key}` : key
}

export function install<R>(Vue: VueConstructor) {
Vue.use(Vuex)
Vue.mixin({ beforeCreate: storeInit })

function storeInit(this: Vue) {
Object.defineProperty(this, '$stock', {
get: (): R => this.$store.getters.$statics
})
}
return `vuexModuleDecorators/${module._vmdModuleName}`
}
10 changes: 9 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
export { VuexModule, getModule } from './vuexmodule'
export { VuexModule, VuexStore, Context } from './vuexmodule'
export { Module } from './module'
export { Submodule } from './submodule'
export { Action } from './action'
export { Mutation } from './mutation'
export { MutationAction } from './mutationaction'
export { config } from './config'
import { VuexModule, VuexStore } from './vuexmodule'
import { install } from './helpers'
export default {
Module: VuexModule,
Store: VuexStore,
install
}
106 changes: 50 additions & 56 deletions src/module/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { GetterTree, Module as Mod, Store } from 'vuex'
import { GetterTree, Module as Mod } from 'vuex'
import { DynamicModuleOptions, ModuleOptions } from '../moduleoptions'
import { stateFactory as sf } from './stateFactory'
import { addPropertiesToObject } from '../helpers'
import {
staticActionGenerators,
staticGetterGenerator,
staticMutationGenerator,
staticStateGenerator
} from './staticGenerators'
import { addPropertiesToObject, getModuleNamespace } from '../helpers'
import { staticModuleGenerator } from './staticGenerators'
import { installStatics, VuexStore } from '../vuexmodule'

function registerDynamicModule<S>(module: Mod<S, any>, modOpt: DynamicModuleOptions) {
if (!modOpt.name) {
Expand All @@ -18,82 +14,80 @@ function registerDynamicModule<S>(module: Mod<S, any>, modOpt: DynamicModuleOpti
throw new Error('Store not provided in decorator options when using dynamic option')
}

modOpt.store.registerModule(
modOpt.name, // TODO: Handle nested modules too in future
module,
{ preserveState: modOpt.preserveState || false }
)
const oldStatics = modOpt.store.getters.$statics
const moduleMap = (modOpt.store as VuexStore<S>)._vmdModuleMap
const path = modOpt.name.split('.')
const namespace = moduleMap ? getModuleNamespace(moduleMap, path, !!modOpt.namespaced) : undefined
modOpt.store.registerModule(path, module, { preserveState: modOpt.preserveState || false })
if (moduleMap && oldStatics) {
installStatics(modOpt.store.getters, moduleMap, oldStatics)
const name = path[path.length - 1]
const recursive = true
const statics = staticModuleGenerator(module, modOpt.store, path, namespace, recursive)
const parentStatics = path.slice(0, -1).reduce((s, key) => s[key], oldStatics)
parentStatics[name] = statics
const parentModuleMap = path.slice(0, -1).reduce((s, key) => s.modules![key], moduleMap)
parentModuleMap.modules![name] = installStatics(
modOpt.store.getters,
module,
statics,
path,
namespace,
recursive
)
}
}

function moduleDecoratorFactory<S>(moduleOptions: ModuleOptions) {
return function<TFunction extends Function>(constructor: TFunction): TFunction | void {
const module: Function & Mod<S, any> = constructor
const stateFactory = () => sf(module)
Object.defineProperty(constructor, 'factory', {
get() {
return stateFactory
}
})

moduleOptions.stateFactory = moduleOptions.stateFactory !== false
if (!module.state) {
module.state = moduleOptions && moduleOptions.stateFactory ? stateFactory : stateFactory()
module.state = moduleOptions.stateFactory ? stateFactory : stateFactory()
}
if (!module.getters) {
module.getters = {} as GetterTree<S, any>
}
if (!module.namespaced) {
module.namespaced = moduleOptions && moduleOptions.namespaced
if (module.namespaced === undefined) {
module.namespaced = moduleOptions.namespaced !== false
}
moduleOptions.namespaced = module.namespaced
Object.getOwnPropertyNames(module.prototype).forEach((funcName: string) => {
const descriptor = Object.getOwnPropertyDescriptor(
module.prototype,
funcName
) as PropertyDescriptor
if (descriptor.get && module.getters) {
const staticKey = '$statics/' + funcName
module.getters[funcName] = function(
state: S,
getters: GetterTree<S, any>,
getters: any,
rootState: any,
rootGetters: GetterTree<any, any>
rootGetters: any
) {
const thisObj = { context: { state, getters, rootState, rootGetters } }
addPropertiesToObject(thisObj, state)
addPropertiesToObject(thisObj, getters)
const got = (descriptor.get as Function).call(thisObj)
const context = { state, getters, rootState, rootGetters }
let moduleAccessor
if (getters[staticKey]) {
moduleAccessor = getters[staticKey]
moduleAccessor.context = context
} else {
moduleAccessor = { context }
addPropertiesToObject(moduleAccessor, state)
addPropertiesToObject(moduleAccessor, getters)
}
const got = (descriptor.get as Function).call(moduleAccessor)
return got
}
}
})
const modOpt = moduleOptions as DynamicModuleOptions
if (modOpt.name) {
Object.defineProperty(constructor, '_genStatic', {
value: (store?: Store<any>) => {
let statics = { store: store || modOpt.store }
if (!statics.store) {
throw new Error(`ERR_STORE_NOT_PROVIDED: To use getModule(), either the module
should be decorated with store in decorator, i.e. @Module({store: store}) or
store should be passed when calling getModule(), i.e. getModule(MyModule, this.$store)`)
}
// =========== For statics ==============
// ------ state -------
staticStateGenerator(module, modOpt, statics)

// ------- getters -------
if (module.getters) {
staticGetterGenerator(module, modOpt, statics)
}

// -------- mutations --------
if (module.mutations) {
staticMutationGenerator(module, modOpt, statics)
}
// -------- actions ---------
if (module.actions) {
staticActionGenerators(module, modOpt, statics)
}
return statics
}
})

Object.defineProperty(constructor, '_vmdModuleName', {
value: modOpt.name
})
}

if (modOpt.dynamic) {
registerDynamicModule(module, modOpt)
Expand Down
22 changes: 18 additions & 4 deletions src/module/stateFactory.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
import { Module as Mod } from 'vuex'

const reservedKeys = ['actions', 'getters', 'mutations', 'modules', 'state', 'namespaced', 'commit']
const reservedKeys = [
'namespaced',
'state',
'getters',
'mutations',
'actions',
'modules',
'commit',
'dispatch',
'factory',
'context',
'namespace',
'path'
]
export function stateFactory<S>(module: Function & Mod<S, any>) {
const state = new module.prototype.constructor({})
const state = new module.prototype.constructor()
const modules = module.modules || {}
const s = {} as S
Object.keys(state).forEach((key: string) => {
if (reservedKeys.indexOf(key) !== -1) {
if (typeof state[key] !== 'undefined') {
throw new Error(
`ERR_RESERVED_STATE_KEY_USED: You cannot use the following
['actions', 'getters', 'mutations', 'modules', 'state', 'namespaced', 'commit']
[${reservedKeys.join(', ')}]
as fields in your module. These are reserved as they have special purpose in Vuex`
)
}
return
}
if (state.hasOwnProperty(key)) {
if (state.hasOwnProperty(key) && !modules.hasOwnProperty(key)) {
if (typeof state[key] !== 'function') {
;(s as any)[key] = state[key]
}
Expand Down
Loading