Skip to content

Commit 39c3999

Browse files
authored
Merge pull request #9 from jaredmcateer/vuex-class
2 parents ab54d52 + 726925a commit 39c3999

File tree

18 files changed

+894
-31
lines changed

18 files changed

+894
-31
lines changed

README.md

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ The files to be converted must meet the criteria below:
5858
- [ ] `@ProvideReactive / @InjectReactive`
5959
- [x] `@Emit`
6060
- [x] `@Ref`
61+
- vuex-class
62+
- [x] `@Action`
63+
- [x] `@Getter`
64+
- [x] `@Mutation`
65+
- [x] `@State`
66+
- `@<namespace>`
67+
- [ ] `.Action`
68+
- [ ] `.Getter`
69+
- [ ] `.Mutation`
70+
- [ ] `.State`
6171
- [x] replace `this` to `props`, `variable`, or `context`.
6272
- [x] sort by dependency.
6373

@@ -113,24 +123,26 @@ const { file, result } = convertFile(
113123

114124
```typescript
115125
{
116-
// root path for calc file absolute path, if in CLI, --root value will replace. default:`process.cwd()`
117-
root?: string
118-
// show debug message. default: `false`
119-
debug?: boolean,
120-
// if true, use @vue/composition-api. default: `false`
121-
compatible?: boolean
122-
// first setup function parameter name. default: `props`
123-
setupPropsKey?: string
124-
// second setup function parameter name. default: `context`
125-
setupContextKey?: string
126-
// Use custom version typescript. default: Typescript 3.7.3
127-
typescript?: typeof ts
128-
// Use custom version vue-template-compiler, please match your project vue versions. default: vue-template-compiler 2.6.11
129-
vueTemplateCompiler?: typeof vueTemplateCompiler
130-
// Use custom prettier config file path. if file does not exist, use default uncouth prettier config. default: `.prettierrc`
131-
prettierConfig?: string
126+
// root path for calc file absolute path, if in CLI, --root value will replace.
127+
root?: string, // Default:`process.cwd()`
128+
// show debug message.
129+
debug?: boolean, // Default: `false`
130+
// if true, use @vue/composition-api.
131+
compatible?: boolean, // Default: `false`
132+
// first setup function parameter name.
133+
setupPropsKey?: string, // Default: `props`
134+
// second setup function parameter name.
135+
setupContextKey?: string, // Default: `context`
136+
// Use custom version typescript.
137+
typescript?: typeof ts, // Default: Typescript 3.7.3
138+
// Use custom version vue-template-compiler, please match your project vue versions.
139+
vueTemplateCompiler?: typeof vueTemplateCompiler, // Default: vue-template-compiler 2.6.11
140+
// Use custom prettier config file path. if file does not exist, use default uncouth prettier config.
141+
prettierConfig?: string // Default: `.prettierrc`
132142
// Use custom ASTConvertPlugins for ASTConvert and ASTTransform
133-
plugins?: ASTConvertPlugins
143+
plugins?: ASTConvertPlugins,
144+
// if using vuex-class the variable name to use for the store
145+
vuexKey?: string, // Default: `store`
134146
}
135147
```
136148

src/convert.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { runPlugins } from "./plugins";
44
import { UncouthOptions } from "./options";
55
import { log } from "./debug";
66

7-
const vueClassModules = ["vue-class-component", "vue-property-decorator"];
7+
const vueClassModules = ["vue-class-component", "vue-property-decorator", "vuex-class"];
88

99
export function convertAST(
1010
sourceFile: ts.SourceFile,

src/helpers/TsHelper.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ts from "typescript";
22
import { UncouthOptions } from "../options";
3+
import { ImportModule } from "../plugins/types";
34
import { isString } from "../utils";
45

56
export class TsHelper {
@@ -15,11 +16,28 @@ export class TsHelper {
1516
return this.module.factory;
1617
}
1718

18-
namedImports(names: string[]): { named: string[]; external: string }[] {
19+
get rocketToken(): ts.PunctuationToken<ts.SyntaxKind.EqualsGreaterThanToken> {
20+
return this.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken);
21+
}
22+
23+
getDecorator(node: ts.Node, decorator: string): ts.Decorator | undefined {
24+
if (!node?.decorators) return;
25+
26+
return node.decorators.find(
27+
(el) => (el.expression as ts.CallExpression).expression.getText() === decorator
28+
);
29+
}
30+
31+
getVueExternal(): "@vue/composition-api" | "vue" {
32+
return this.compatible ? "@vue/composition-api" : "vue";
33+
}
34+
35+
namedImports(names: string[], external?: string): ImportModule[] {
36+
external = external || this.getVueExternal();
1937
return [
2038
{
2139
named: names,
22-
external: this.compatible ? "@vue/composition-api" : "vue",
40+
external,
2341
},
2442
];
2543
}

src/options.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export interface UncouthOptions {
1212
typescript: typeof ts;
1313
vueTemplateCompiler: typeof vueTemplateCompiler;
1414
prettierConfig: string;
15+
vuexKey: string;
1516
plugins: ASTConvertPlugins;
1617
}
1718

@@ -28,6 +29,7 @@ export function getDefaultUncouthOptions(tsModule: typeof ts = ts): UncouthOptio
2829
vueTemplateCompiler: vueTemplateCompiler,
2930
prettierConfig: ".prettierrc",
3031
plugins: getDefaultPlugins(tsModule),
32+
vuexKey: "store",
3133
};
3234
}
3335

src/plugins/index.ts

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ import { convertInject } from "./vue-property-decorator/Inject";
2222
import { convertProvide } from "./vue-property-decorator/Provide";
2323
import { convertTemplateRef } from "./vue-class-component/TemplateRef";
2424
import { TsHelper } from "../helpers/TsHelper";
25+
import { convertVuexAction } from "./vuex-class/Action";
26+
import { convertVuexGetter } from "./vuex-class/Getter";
27+
import { convertVuexState } from "./vuex-class/State";
28+
import { convertVuexMutation } from "./vuex-class/Mutation";
2529

2630
export function getDefaultPlugins(tsModule: typeof ts): ASTConvertPlugins {
2731
return {
@@ -41,6 +45,10 @@ export function getDefaultPlugins(tsModule: typeof ts): ASTConvertPlugins {
4145
convertInject,
4246
convertData,
4347
convertTemplateRef,
48+
convertVuexAction,
49+
convertVuexGetter,
50+
convertVuexState,
51+
convertVuexMutation,
4452
],
4553
[tsModule.SyntaxKind.GetAccessor]: [convertGetter],
4654
[tsModule.SyntaxKind.SetAccessor]: [convertSetter],
@@ -132,6 +140,8 @@ export function convertASTResultToSetupFn(
132140
): ts.MethodDeclaration {
133141
const $t = new TsHelper(options);
134142

143+
const composables = getComposables(astResults, $t);
144+
135145
const returnStatement = $t.addTodoComment(
136146
$t.factory.createReturnStatement(
137147
$t.factory.createObjectLiteralExpression([
@@ -149,6 +159,7 @@ export function convertASTResultToSetupFn(
149159
"setup",
150160
[options.setupPropsKey, options.setupContextKey],
151161
[
162+
...composables,
152163
...(astResults
153164
.filter((el) => el.kind === ASTResultKind.COMPOSITION)
154165
.map((el) => el.nodes)
@@ -158,15 +169,74 @@ export function convertASTResultToSetupFn(
158169
);
159170
}
160171

172+
interface Clause {
173+
named: Set<string>;
174+
default?: string;
175+
params?: ts.Expression[];
176+
}
177+
178+
function getComposables(astResults: ASTResult<ts.Node>[], $t: TsHelper): ts.VariableStatement[] {
179+
const composableMap = new Map<string, Clause>();
180+
astResults.forEach((result) => {
181+
if (!result.composables) return;
182+
183+
result.composables.forEach((info) => {
184+
const func = info.func;
185+
const tmp: Clause = composableMap.get(func) ?? { named: new Set() };
186+
187+
if (!("default" in tmp) && "default" in info) {
188+
tmp.default = info.default;
189+
}
190+
191+
if ("params" in info) {
192+
tmp.params = info.params;
193+
}
194+
195+
if ("named" in info) {
196+
info.named?.forEach((name) => tmp.named.add(name));
197+
}
198+
199+
composableMap.set(func, tmp);
200+
});
201+
});
202+
203+
const composables = Array.from(composableMap)
204+
.map(([func, clause]) => {
205+
const funcExpr = $t.createCallExpression(func, undefined, clause.params);
206+
const statements: ts.VariableStatement[] = [];
207+
if (clause.default) {
208+
statements.push($t.createConstStatement(clause.default, funcExpr));
209+
} else {
210+
const u = undefined;
211+
const importElements = [...clause.named].map((name) =>
212+
$t.factory.createBindingElement(u, u, $t.factory.createIdentifier(name))
213+
);
214+
215+
if (importElements.length > 0) {
216+
const vars = $t.factory.createObjectBindingPattern(importElements);
217+
const boundElementsDeclaration = $t.factory.createVariableDeclaration(
218+
vars,
219+
u,
220+
u,
221+
funcExpr
222+
);
223+
const varDecList = $t.factory.createVariableDeclarationList([boundElementsDeclaration]);
224+
statements.push($t.factory.createVariableStatement(u, varDecList));
225+
}
226+
}
227+
228+
return statements;
229+
})
230+
.flat()
231+
.filter((composable): composable is ts.VariableStatement => !!composable);
232+
233+
return composables;
234+
}
235+
161236
export function convertASTResultToImport(
162237
astResults: ASTResult<ts.Node>[],
163238
options: UncouthOptions
164239
): ts.ImportDeclaration[] {
165-
interface Clause {
166-
named: Set<string>;
167-
default?: string;
168-
}
169-
170240
const $t = new TsHelper(options);
171241

172242
const importMap = new Map<string, Clause>();
@@ -177,10 +247,15 @@ export function convertASTResultToImport(
177247
if (!("default" in temp) && "default" in importInfo) {
178248
temp.default = importInfo.default;
179249
}
180-
temp.named.add("defineComponent");
250+
251+
if (key === "vue") {
252+
temp.named.add("defineComponent");
253+
}
254+
181255
for (const named of importInfo.named || []) {
182256
temp.named.add(named);
183257
}
258+
184259
importMap.set(key, temp);
185260
}
186261
}

src/plugins/types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,24 @@ export type ImportModule =
2828
external: string;
2929
};
3030

31+
export type ComposableStatement =
32+
| {
33+
default?: string;
34+
func: string;
35+
params?: ts.Expression[];
36+
}
37+
| {
38+
named?: string[];
39+
func: string;
40+
params?: ts.Expression[];
41+
};
3142
export interface ASTResultBase {
3243
imports: ImportModule[];
3344
kind: ASTResultKind;
3445
reference: ReferenceKind;
3546
attributes: string[];
3647
tag: string;
48+
composables?: ComposableStatement[];
3749
}
3850

3951
export interface ASTResultToObject<N = ts.PropertyAssignment> extends ASTResultBase {

src/plugins/vuex-class/Action.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { convertVuexMethodFactory } from "./convertMethodFactory";
2+
3+
/**
4+
* @example
5+
* \@Action('myNamespace/someAction') doSomeAction: (text: string) => Promise<void>
6+
* // converts to
7+
* const doSomeAction = async (text: string): Promise<void> => {
8+
* return await store.dispatch('myNamespace/someAction', text);
9+
* }
10+
* @param node
11+
* @param options
12+
* @returns
13+
*/
14+
export const convertVuexAction = convertVuexMethodFactory("Action", "dispatch");

src/plugins/vuex-class/Getter.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { convertVuexComputedFactory } from "./convertComputedFactory";
2+
3+
/**
4+
* @example
5+
* \@Getter('myNamespace/someGetter') getSomething: boolean;
6+
* // converts to
7+
* const getSomething = computed<boolean>(() => {
8+
* return store.getter('myNamespace/someGetter');
9+
* })
10+
*/
11+
export const convertVuexGetter = convertVuexComputedFactory("Getter", "getters");

src/plugins/vuex-class/Mutation.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { convertVuexMethodFactory } from "./convertMethodFactory";
2+
3+
/**
4+
* @example
5+
* \@Mutation('myNamespace/SOME_MUTATION') mutateSomething: (text: string) => void
6+
* // converts to
7+
* const mutateSomething = (text: string): void => {
8+
* return store.commit('myNamespace/SOME_MUTATION', text);
9+
* }
10+
* @param node
11+
* @param options
12+
* @returns
13+
*/
14+
export const convertVuexMutation = convertVuexMethodFactory("Mutation", "commit");

src/plugins/vuex-class/State.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { convertVuexComputedFactory } from "./convertComputedFactory";
2+
3+
/**
4+
* @example
5+
* \@State('myNamespace/stateA') sA: number;
6+
* // converts to
7+
* const sA = computed(() => {
8+
* return store.state.stateA;
9+
* });
10+
*/
11+
export const convertVuexState = convertVuexComputedFactory("State", "state");

0 commit comments

Comments
 (0)