Skip to content

Commit 0400d0f

Browse files
author
Ben Crowl
committed
Builder underway
0 parents  commit 0400d0f

File tree

13 files changed

+322
-0
lines changed

13 files changed

+322
-0
lines changed

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
node_modules/**/*
2+
coverage/**/*
3+
.nyc_output/**/*
4+
npm-debug.log
5+
src/**/*.js
6+
src/**/*.js.map
7+
dist/**

.vscode/settings.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Place your settings in this file to overwrite default and user settings.
2+
{
3+
"typescript.format.placeOpenBraceOnNewLineForFunctions": true,
4+
"typescript.format.placeOpenBraceOnNewLineForControlBlocks": true,
5+
"tsimporter.emitSemicolon": false,
6+
"tsimporter.filesToExclude": [
7+
"./dist/**/*"
8+
]
9+
}

.vscode/tasks.json

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"version": "0.1.0",
3+
"command": "npm",
4+
"isShellCommand": true,
5+
"args": ["run"],
6+
"showOutput": "always",
7+
"tasks": [
8+
{
9+
"taskName": "build",
10+
"isBuildCommand": true
11+
},
12+
// {
13+
// "taskName": "clean"
14+
// },
15+
// {
16+
// "taskName": "lint"
17+
// },
18+
// {
19+
// "taskName": "coverage"
20+
// },
21+
{
22+
"taskName": "test",
23+
"isTestCommand": true
24+
}
25+
]
26+
}

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Vuex-Typescript-Builder
2+
3+
[https://github.com/mrcrowl/vuex-ts-builder/](https://github.com/mrcrowl/vuex-ts-builder/)
4+
5+
A shameless extension of the ideas presented by [@istrib](https://github.com/istrib) in the [vuex-typescript](https://github.com/istrib/vuex-typescript) package.

package.json

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"name": "vuex-ts-builder",
3+
"version": "1.0.0",
4+
"description": "Typescript builder for Vuex Store modules",
5+
"files": [
6+
"dist/index.js",
7+
"dist/index.d.ts"
8+
],
9+
"main": "dist/index.js",
10+
"typings": "dist/index.d.ts",
11+
"dependencies": {
12+
"vuex": "^2.3.1"
13+
},
14+
"devDependencies": {
15+
"@types/chai": "^3.4.35",
16+
"@types/chai-http": "^0.0.30",
17+
"@types/mocha": "^2.2.40",
18+
"chai": "^3.5.0",
19+
"mocha": "^3.2.0",
20+
"nyc": "^10.1.2",
21+
"rimraf": "^2.6.1",
22+
"typescript": "^2.2.1",
23+
"vue": "^2.3.0"
24+
},
25+
"repository": {
26+
"type": "git",
27+
"url": "git+https://github.com/mrcrowl/vuex-ts-builder.git"
28+
},
29+
"keywords": [
30+
"vuex",
31+
"typescript",
32+
"builder"
33+
],
34+
"author": "mrcrowl",
35+
"license": "MIT",
36+
"bugs": {
37+
"url": "https://github.com/mrcrowl/vuex-ts-builder/issues"
38+
},
39+
"homepage": "https://github.com/mrcrowl/vuex-ts-builder#readme",
40+
"scripts": {
41+
"clean": "rimraf dist && rimraf coverage",
42+
"compile": "tsc --pretty",
43+
"build": "npm run clean && npm run lint-force && npm run compile",
44+
"test": "npm run coverage",
45+
"test-only": "mocha --recursive dist/**/*.spec.js",
46+
"test-watch": "npm run test-only -- --watch",
47+
"test-debug": "mocha --debug-brk --debug=0.0.0.0:5858 dist/**/*.spec.js",
48+
"coverage": "rimraf coverage && nyc --reporter=html --reporter=text --reporter=lcov -x **/*.spec.js npm run test-only"
49+
}
50+
}

src/index.ts

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
2+
import { ActionContext, ActionTree, GetterTree, Module, MutationTree, Store } from "vuex"
3+
4+
const useRootNamespace = { root: true }
5+
6+
export type MutationHandler<S, P> = (state: S, payload: P) => void
7+
// export type PayloadlessMutationHandler<S> = (state: S) => void
8+
9+
export type ActionHandler<S, R, P, T> = (context: BareActionContext<S, R>, payload: P) => Promise<T>
10+
// export type PayloadlessActionHandler<S, R, T> = (context: BareActionContext<S, R>) => Promise<T>
11+
// export type VoidActionHandler<S, R, P> = (context: BareActionContext<S, R>, payload: P) => void
12+
// export type VoidPayloadlessActionHandler<S, R> = (context: BareActionContext<S, R>) => void
13+
export type GetterHandler<S, R, T> = (state: S, rootState: R) => T
14+
15+
export interface BareActionContext<S, R>
16+
{
17+
state: S;
18+
rootState: R;
19+
}
20+
21+
export class ModuleBuilder<S, R> {
22+
private store: Store<R>
23+
24+
private getters: GetterTree<S, R> = {}
25+
private mutations: MutationTree<S> = {}
26+
private actions: ActionTree<S, R> = {}
27+
28+
constructor(private namespace: string, private state: S) { }
29+
30+
provideStore(store: Store<R>)
31+
{
32+
this.store = store
33+
}
34+
35+
commit<P>(handler: MutationHandler<S, void>): () => void
36+
commit<P>(handler: MutationHandler<S, P>): (payload: P) => void
37+
commit<P>(handler: MutationHandler<S, P>)
38+
{
39+
const key = qualifyKey(handler, this.namespace)
40+
return ((payload: P) => this.store.commit(key, payload, useRootNamespace)) as any
41+
}
42+
43+
dispatch<P, T>(handler: ActionHandler<S, R, void, void>): () => Promise<void>
44+
dispatch<P, T>(handler: ActionHandler<S, R, P, void>): (payload: P) => Promise<void>
45+
dispatch<P, T>(handler: ActionHandler<S, R, void, T>): () => Promise<T>
46+
dispatch<P, T>(handler: ActionHandler<S, R, P, T>): (payload: P) => Promise<T>
47+
dispatch<P, T>(handler: any): any
48+
{
49+
const key = qualifyKey(handler, this.namespace)
50+
return (payload: P) => this.store.dispatch(key, payload, useRootNamespace)
51+
}
52+
53+
read<T>(handler: GetterHandler<S, R, T>): () => T
54+
read<T>(handler: GetterHandler<S, R, T>, name: string): () => T
55+
read<T>(handler: GetterHandler<S, R, T>, name?: string): () => T
56+
{
57+
const key = qualifyKey(handler, this.namespace, name)
58+
return () => this.store.getters[key] as T
59+
}
60+
61+
toVuexModule(): Module<S, R>
62+
{
63+
return {
64+
namespaced: true,
65+
state: this.state,
66+
getters: this.getters,
67+
mutations: this.mutations,
68+
actions: this.actions
69+
}
70+
}
71+
}
72+
73+
function qualifyKey(handler: Function, namespace: string | undefined, name?: string)
74+
{
75+
const key: string = name || handler.name
76+
if (!key)
77+
{
78+
throw new Error(`Vuex handler functions must not be anonymous. Possible causes: fat-arrow functions, uglify`)
79+
}
80+
return namespace ? `${namespace}/${key}` : key
81+
}

src/tests/store.spec.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { expect } from "chai";
2+
import * as Vue from "vue";
3+
import * as Vuex from "vuex";
4+
import { createStore, State } from "./store";
5+
import * as basket from "./store/basket";

src/tests/store/auth/auth.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
import { ModuleBuilder } from "../../.."
3+
import { BirthdayState } from "./state"
4+
5+
6+
const birthday = new ModuleBuilder<AuthState>

src/tests/store/auth/state.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
export interface AuthState
3+
{
4+
userID: string
5+
}

src/tests/store/birthday/birthday.ts

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
2+
import { ModuleBuilder } from "../../.."
3+
import { BirthdayState, Birthday } from "./state"
4+
import { Module } from "vuex"
5+
import { RootState } from "../root"
6+
7+
const initialState: BirthdayState = {
8+
birthdays: [
9+
{
10+
name: "Jacob",
11+
dob: new Date(2006, 10, 11)
12+
},
13+
{
14+
name: "Danny",
15+
dob: new Date(2009, 11, 30)
16+
}
17+
]
18+
}
19+
20+
const builder = new ModuleBuilder<BirthdayState, RootState>("birthday", initialState)
21+
22+
const commitAddBirthday = builder.commit((state, payload: { birthday: Birthday }) =>
23+
{
24+
state.birthdays.push(payload.birthday)
25+
})
26+
27+
const commitRemoveFirstBirthday = builder.commit((state) =>
28+
{
29+
state.birthdays.shift()
30+
})
31+
32+
const getOldestName = builder.read((state): Birthday | undefined =>
33+
{
34+
const sortedBirthdays = (<Birthday[]>[]).sort((a, b) => a.dob.getTime() - b.dob.getTime())
35+
return sortedBirthdays[0]
36+
})
37+
38+
const getDOBforName = builder.read((state) => (name: string) =>
39+
{
40+
const matches = state.birthdays.filter(b => b.name === name)
41+
if (matches.length)
42+
{
43+
return matches[0].dob
44+
}
45+
46+
return
47+
}, "dob")
48+
49+
const dispatchRemoveFirstAfter = builder.dispatch(async (context, delay: number) =>
50+
{
51+
if (context.state.birthdays.length > 2)
52+
{
53+
await new Promise((resolve, _) => setTimeout(resolve, 1000)); // second delay
54+
commitRemoveFirstBirthday()
55+
}
56+
})
57+
58+
const module = {
59+
// getters + methods
60+
get oldestName() { return getOldestName() },
61+
dateOfBirthFor(name: string) { return getDOBforName()(name) },
62+
63+
// mutations
64+
commitAddBirthday,
65+
commitRemoveFirstBirthday,
66+
67+
// actions
68+
dispatchRemoveFirstAfter,
69+
}
70+
71+
module.commitAddBirthday({ birthday: { dob: new Date(1980, 2, 3), name: "Louise" } })
72+
module.commitRemoveFirstBirthday()
73+
module.dateOfBirthFor("Louise")
74+
module.dispatchRemoveFirstAfter(1000)
75+
76+
export default module
77+
78+
export const vuexModule: Module<BirthdayState, RootState> = builder.toVuexModule()

src/tests/store/birthday/state.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
export interface Birthday
3+
{
4+
name: string
5+
dob: Date
6+
}
7+
8+
export interface BirthdayState
9+
{
10+
birthdays: Birthday[]
11+
}

src/tests/store/root.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
import { AuthState } from "./auth/state";
3+
import { BirthdayState } from "../store/birthday/state"
4+
5+
export interface RootState
6+
{
7+
auth: AuthState
8+
birthday: BirthdayState
9+
}

tsconfig.json

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"compileOnSave": true,
3+
"compilerOptions": {
4+
"module": "commonjs",
5+
"moduleResolution": "node",
6+
"lib": [
7+
"es2015"
8+
],
9+
// "strict": true,
10+
"noImplicitAny": true,
11+
"noImplicitReturns": true,
12+
"noImplicitThis": true,
13+
"strictNullChecks": true,
14+
"target": "es6",
15+
"sourceMap": true,
16+
"outDir": "./dist",
17+
"declaration": true,
18+
"removeComments": true,
19+
"skipLibCheck": true,
20+
"typeRoots": [
21+
"./node_modules/@types"
22+
]
23+
},
24+
"include": [
25+
"src"
26+
],
27+
"exclude": [
28+
"node_modules"
29+
]
30+
}

0 commit comments

Comments
 (0)