Skip to content

Commit af84b98

Browse files
committed
about to refractor return class
1 parent df6a0e6 commit af84b98

File tree

4 files changed

+150
-50
lines changed

4 files changed

+150
-50
lines changed

src/interfaces.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,28 @@ export interface VuexModuleOptions {
66
export class VuexModule {
77
}
88

9+
type VuexStashTypes = "state" | "explicit-mutation" | "getter" | "action" | "submodule";
10+
11+
export interface VuexStash<T extends VuexStashTypes> {
12+
[ field :string ]: {
13+
type :T,
14+
value:
15+
T extends "state" ? boolean | number | string | object | any :
16+
T extends "explicit-mutation" ? ( state :any, payload :any ) => void :
17+
T extends "getter" ? { getter :( state :any, context :any ) => any, mutation? :( state :any, payload :any ) => void } :
18+
T extends "action" ? ( context :any, payload :any ) => Promise<any> :
19+
T extends "submodule" ? Map :
20+
never
21+
}
22+
}
23+
924
export type VuexModuleConstructor = typeof VuexModule & VuexModuleInternals;
1025

1126
export interface VuexModuleInternals {
1227
prototype :{
1328
__options__ :VuexModuleOptions | undefined;
1429
__vuex_module_cache__ :undefined | VuexObject;
30+
__vuex_module_tree_stash__: VuexStash<VuexStashTypes>;
1531
__vuex_proxy_cache__ :{} | undefined;
1632
__vuex_local_proxy_cache__ :{} | undefined;
1733
__submodules_cache__: Map;

src/module.ts

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ export function initializeStore( options ?:VuexModuleOptions ) {
1212
*/
1313
(VuexModule as VuexModuleConstructor).prototype.__options__ = options;
1414
(VuexModule as VuexModuleConstructor).prototype.__vuex_module_cache__ = undefined;
15+
(VuexModule as VuexModuleConstructor).prototype.__vuex_module_tree_stash__ = {};
1516
(VuexModule as VuexModuleConstructor).prototype.__vuex_proxy_cache__ = undefined;
1617
(VuexModule as VuexModuleConstructor).prototype.__vuex_local_proxy_cache__ = undefined;
17-
(VuexModule as VuexModuleConstructor).prototype.__context_store__ = undefined;
18+
(VuexModule as VuexModuleConstructor).prototype.__context_store__ = {};
19+
(VuexModule as VuexModuleConstructor).prototype.__submodules_cache__ = {};
1820
(VuexModule as VuexModuleConstructor).prototype.__mutations_cache__ = {
1921
__explicit_mutations__: {},
2022
__setter_mutations__: {}
@@ -24,7 +26,7 @@ export function initializeStore( options ?:VuexModuleOptions ) {
2426

2527
}
2628

27-
export function extractModule( cls :typeof VuexModule ) {
29+
export function extractVuexModule( cls :typeof VuexModule ) {
2830

2931
const VuexClass = cls as VuexModuleConstructor;
3032

@@ -35,19 +37,21 @@ export function extractModule( cls :typeof VuexModule ) {
3537
}
3638

3739
// If not extract vuex module from class.
38-
const fromInstance = extractFromInstance( VuexClass );
39-
const fromPrototype = extractFromPrototype( VuexClass );
40+
const fromInstance = extractModulesFromInstance( VuexClass );
41+
const fromPrototype = extractModulesFromPrototype( VuexClass );
4042

4143
// Cache explicit mutations and getter mutations.
4244
VuexClass.prototype.__mutations_cache__.__explicit_mutations__ = fromInstance.mutations;
4345
VuexClass.prototype.__mutations_cache__.__setter_mutations__ = fromPrototype.mutations;
4446

47+
console.log( "Vuex Stash", VuexClass.name ,VuexClass.prototype.__vuex_module_tree_stash__ )
48+
4549
const vuexModule :VuexObject = {
4650
namespaced: VuexClass.prototype.__options__ ? VuexClass.prototype.__options__.namespaced : false,
4751
state: fromInstance.state,
4852
mutations: { ...fromInstance.mutations, ...fromPrototype.mutations, __internal_mutator__: internalMutator },
4953
getters: { ...fromPrototype.getters, __internal_getter__: internalGetter },
50-
actions: fromPrototype.actions,
54+
actions: { ...fromPrototype.actions, __internal_action__: internalAction },
5155
modules: fromInstance.submodules,
5256
};
5357

@@ -64,14 +68,16 @@ export function toCamelCase(str :string){
6468
return str[ 0 ].toLocaleLowerCase() + str.substring( 1 );
6569
}
6670

67-
function extractFromInstance( cls :VuexModuleConstructor ) {
71+
function extractModulesFromInstance( cls :VuexModuleConstructor ) {
6872

6973
const instance = new cls() as InstanceType<VuexModuleConstructor> & Map;
7074
const classFields = Object.getOwnPropertyNames( instance );
7175
const state :Map = {};
7276
const mutations :Map = {};
7377
const submodules :Map = {};
78+
const submodulesCache = cls.prototype.__submodules_cache__;
7479
const moduleOptions = cls.prototype.__options__ || {};
80+
const vuexModuleStash = cls.prototype.__vuex_module_tree_stash__;
7581

7682
for( let field of classFields ) {
7783
/**
@@ -83,20 +89,40 @@ function extractFromInstance( cls :VuexModuleConstructor ) {
8389
// Check if field is a submodule.
8490
const fieldIsSubModule = isFieldASubModule( instance, field );
8591
if( fieldIsSubModule ) {
86-
submodules[ field ] = extractVuexSubModule( instance, field );
92+
93+
// Cache submodule class
94+
submodulesCache[ field ] = instance[ field ][ "__submodule_class__" ]
95+
96+
const submodule = extractVuexSubModule( instance, field );
97+
98+
submodules[ field ] = submodule;
99+
100+
// Also stash the submodule in the vuex module stash.
101+
// vuexModuleStash[ field ] = { type: "submodule", value: submodule }
102+
87103
continue;
88104
}
89105

90106
// Check if field is an explicit mutation.
91107
if( typeof instance[ field ] === "function" ) {
92-
mutations[ field ] = ( state :any, payload :any ) => instance[ field ].call( state, payload );
108+
const mutation = ( state :any, payload :any ) => instance[ field ].call( state, payload );
109+
110+
// Stash mutation in vuex module stash.
111+
vuexModuleStash[ field ] = { type: "explicit-mutation", value: mutation };
112+
113+
mutations[ field ] = mutation;
93114
continue;
94115
}
95116

117+
118+
console.log( "State field:", field, "Class :", cls.name, "constructor:", cls.prototype[ "constructor" ] );
119+
96120
// If field is not a submodule, then it must be a state.
97121
// Check if the vuex module is targeting nuxt. if not define state as normal.
98-
if( moduleOptions.nuxt === true ) state[ field ] = () => instance[ field ];
122+
if( moduleOptions.target === "nuxt" ) state[ field ] = () => instance[ field ];
99123
else state[ field ] = instance[ field ];
124+
// Stash state in vuex module stash.
125+
vuexModuleStash[ field ] = { type: "state", value: instance[ field ] };
100126

101127
}
102128

@@ -107,12 +133,14 @@ function extractFromInstance( cls :VuexModuleConstructor ) {
107133
}
108134
}
109135

110-
function extractFromPrototype( cls :VuexModuleConstructor ) {
136+
function extractModulesFromPrototype( cls :VuexModuleConstructor ) {
111137

112138
const actions :Record<DictionaryField, any> = {};
113139
const mutations :Record<DictionaryField, any>= {};
114140
const getters :Record<DictionaryField, any> = {};
115141
const descriptors :PropertyDescriptorMap = getDescriptors( cls.prototype );
142+
const vuexModuleStash = cls.prototype.__vuex_module_tree_stash__;
143+
const gettersList :string[] = Object.keys( descriptors ).filter( field => descriptors[ field ].get );
116144

117145
for( let field in descriptors ) {
118146

@@ -134,26 +162,70 @@ function extractFromPrototype( cls :VuexModuleConstructor ) {
134162
// If proptotype field is a function, extract as an action.
135163
if( typeof descriptor.value === "function" ) {
136164
const func = descriptor.value as Function
137-
actions[ field ] = function( context :any, payload :any ) {
165+
166+
const action = function( context :any, payload :any ) {
138167
cls.prototype.__context_store__ = context;
139168
const proxy = createLocalProxy( cls, context );
140169
return func.call( proxy, payload )
141170
}
142171

172+
// Stash action in vuex module stash
173+
vuexModuleStash[ field ] = { type: "action", value: action };
174+
175+
actions[ field ] = action;
176+
143177
continue;
144178
}
145179

146180
// If the prototype field has a getter.
147181
if( descriptor.get ) {
148-
getters[ field ] = ( state :any, context :Map ) => {
182+
const getter = ( state :any, context :Map ) => {
149183
const proxy = createLocalProxy( cls, context )
150184
return descriptor.get!.call( proxy )
151185
}
186+
187+
// Cache getter in vuex store.
188+
if( vuexModuleStash[ field ] ) vuexModuleStash[ field ].value.getter = getter;
189+
else vuexModuleStash[ field ] = { type: "getter", value: { getter, }}
190+
191+
getters[ field ] = getter;
152192
}
153193

154-
// if the prototype field has an explicit mutation (i.e setter).
194+
// if the prototype field has setter mutation.
155195
if( descriptor.set ) {
156-
mutations[ field ] = (state :any, payload :any) => descriptor.set!.call( state, payload )
196+
const mutation = (state :any, payload :any) => descriptor.set!.call( state, payload );
197+
198+
// Before we push a setter mutation We must verify
199+
// if that mutation has a corresponding getter.
200+
// If not, we dissallow it.
201+
202+
const mutationHasGetter = gettersList.indexOf( field ) > -1;
203+
if( mutationHasGetter === false ) {
204+
// Throw an Error.
205+
throw new Error(
206+
`\nImproper Use of Setter Mutations:\n` +
207+
`at >>\n` +
208+
`set ${ field }( payload ) {\n` +
209+
`\t...\n` +
210+
`}\n` +
211+
`\n` +
212+
`Setter mutations should only be used if there is a corresponding getter defined.\n` +
213+
`\n` +
214+
`Either define a corresponding getter for this setter mutation or,\n` +
215+
`Define them as an explicit mutation using function assignment.\n` +
216+
`Example:\n` +
217+
`--------------------\n` +
218+
`${ field } = ( payload ) => {\n` +
219+
` ...\n` +
220+
`}`
221+
)
222+
}
223+
224+
// stash the mutation in the vuex module stash
225+
if( vuexModuleStash[ field ] ) vuexModuleStash[ field ].value.mutation = mutation;
226+
else vuexModuleStash[ field ] = { type: "getter", value: { mutation } }
227+
228+
mutations[ field ] = mutation;
157229
}
158230

159231
}
@@ -219,3 +291,5 @@ const internalGetter = ( state :any, context :any ) => ( field :string ) => {
219291

220292
}
221293
}
294+
295+
const internalAction = ( state :any, context :any ) => undefined

src/proxy.ts

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { extractModule, toCamelCase } from "./module";
1+
import { extractVuexModule, toCamelCase } from "./module";
22
import { VuexModuleConstructor, Map, VuexModule } from "./interfaces"
33

44

@@ -43,19 +43,30 @@ export function _createProxy<T>(cls: T, $store: any, namespacedPath = "") {
4343
//@ts-ignore
4444
const VuexClass = cls as VuexModuleConstructor;
4545
const proxy = {};
46-
const { state, mutations, actions, getters } = extractModule(VuexClass);
46+
const { state, mutations, actions, getters, modules } = extractVuexModule( VuexClass );
4747

4848
createGettersAndMutationProxyFromState({ cls: VuexClass, proxy, state, $store, namespacedPath });
4949
createExplicitMutationsProxy({ cls: VuexClass, proxy, $store, namespacedPath });
5050
createGettersAndGetterMutationsProxy({ cls: VuexClass, mutations, getters, proxy, $store, namespacedPath });
5151
createActionProxy({ actions, proxy, $store, namespacedPath });
52+
53+
createSubModuleProxy( $store, VuexClass, proxy, modules );
5254

53-
runSetterCheck( VuexClass, getters )
5455

5556
//@ts-ignore
5657
return proxy as InstanceType<T>;
5758
}
5859

60+
61+
function createSubModuleProxy( $store :Map, cls:VuexModuleConstructor, proxy :Map, modules :Map ) {
62+
63+
for( let field in modules ) {
64+
const subModuleClass = cls.prototype.__submodules_cache__[ field ];
65+
proxy[ field ] = createProxy( subModuleClass, $store );
66+
}
67+
68+
}
69+
5970
function createGettersAndMutationProxyFromState({ cls, proxy, state, $store, namespacedPath = "", currentField = "" }: { cls: VuexModuleConstructor, proxy: Map; state: Map; $store: any; namespacedPath?: string; currentField?: string; }) {
6071
/**
6172
* 1. Go through all fields in the object and check the values of those fields.
@@ -98,7 +109,7 @@ function createGettersAndMutationProxyFromState({ cls, proxy, state, $store, nam
98109
}
99110

100111
proxy[field] = {};
101-
createGettersAndMutationProxyFromState({ proxy: proxy[field], state: value, $store, namespacedPath, currentField: currentField + field });
112+
createGettersAndMutationProxyFromState({ cls, proxy: proxy[field], state: value, $store, namespacedPath, currentField: currentField + field });
102113

103114
}
104115

@@ -122,8 +133,6 @@ function createGettersAndGetterMutationsProxy({ cls, getters, mutations, proxy,
122133
$store.__internal_mutator__ = mutations.__internal_mutator__;
123134
}
124135

125-
console.log( "proxy <<", $store );
126-
127136
for( let field in getters ) {
128137

129138

@@ -162,30 +171,31 @@ function runSetterCheck( cls :VuexModuleConstructor, getters :Map ) {
162171
// if there are setters defined that are not in getters.
163172
// throw an error.
164173
const setterMutations = cls.prototype.__mutations_cache__.__setter_mutations__;
165-
for( let field in setterMutations ) {
166-
const setterIsNotInGetters = Object.keys( getters ).indexOf( field ) < 0;
167-
if( setterIsNotInGetters ) {
168-
169-
throw new Error(
170-
`\nImproper Use of Setter Mutations:\n` +
171-
`at >>\n` +
172-
`set ${ field }( payload ) {\n` +
173-
`\t...\n` +
174-
`}\n` +
175-
`\n` +
176-
`Setter mutations should only be used if there is a corresponding getter defined.\n` +
177-
`\n` +
178-
`Either define a corresponding getter for this setter mutation or,\n` +
179-
`Define them as an explicit mutation using function assignment.\n` +
180-
`Example:\n` +
181-
`--------------------\n` +
182-
`${ field } = ( payload ) => {\n` +
183-
` ...\n` +
184-
`}`
185-
)
186-
187-
}
188-
}
174+
console.log( "Setter Mutations", cls.name, setterMutations );
175+
// for( let field in setterMutations ) {
176+
// const setterIsNotInGetters = Object.keys( getters ).indexOf( field ) < 0;
177+
// if( setterIsNotInGetters ) {
178+
179+
// throw new Error(
180+
// `\nImproper Use of Setter Mutations:\n` +
181+
// `at >>\n` +
182+
// `set ${ field }( payload ) {\n` +
183+
// `\t...\n` +
184+
// `}\n` +
185+
// `\n` +
186+
// `Setter mutations should only be used if there is a corresponding getter defined.\n` +
187+
// `\n` +
188+
// `Either define a corresponding getter for this setter mutation or,\n` +
189+
// `Define them as an explicit mutation using function assignment.\n` +
190+
// `Example:\n` +
191+
// `--------------------\n` +
192+
// `${ field } = ( payload ) => {\n` +
193+
// ` ...\n` +
194+
// `}`
195+
// )
196+
197+
// }
198+
// }
189199
}
190200

191201
interface ProxyCreator {

src/submodule.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import { VuexModule, DictionaryField, SubModuleType } from "./interfaces";
2-
import { extractModule } from "./module";
1+
import { VuexModule, DictionaryField, SubModuleType, Map, VuexObject } from "./interfaces";
2+
import { extractVuexModule, toCamelCase } from "./module";
33

4-
export function isFieldASubModule( instance :VuexModule, field :DictionaryField ) {
4+
export function isFieldASubModule( instance :VuexModule & Map, field :string ) {
55
return(
66
typeof instance[ field ] === "object" &&
77
instance[ field ][ "__submodule_type__" ] === "submodule"
88
)
99
}
1010

11-
export function extractVuexSubModule( instance :VuexModule, field :DictionaryField ) {
12-
const subModuleClass = instance[ field ][ "__submodule_class__" ];
13-
return extractModule( subModuleClass )
11+
export function extractVuexSubModule( instance :VuexModule & Map, field :string ) {
12+
const subModuleClass = instance[ field ][ "__submodule_class__" ];
13+
return extractVuexModule( subModuleClass )[ toCamelCase( subModuleClass.name ) ] as VuexObject
1414
}
1515

1616
export function createSubModule<T>( Cls :T ) {

0 commit comments

Comments
 (0)