@zachacious/protoc-gen-connect-vue 1.0.8 → 1.0.10
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/dist/generator.js +89 -86
- package/package.json +1 -1
- package/templates/api.ts.mustache +13 -41
- package/templates/rpc.ts.mustache +20 -21
package/dist/generator.js
CHANGED
|
@@ -336793,96 +336793,100 @@ var mustache_default = mustache;
|
|
|
336793
336793
|
|
|
336794
336794
|
// src/generator.ts
|
|
336795
336795
|
var METHOD_KIND_UNARY = 1;
|
|
336796
|
-
var
|
|
336797
|
-
"google.protobuf.Empty"
|
|
336798
|
-
"google.protobuf.Timestamp"
|
|
336799
|
-
"google.protobuf.Duration"
|
|
336800
|
-
|
|
336796
|
+
var KNOWN_WKT = [
|
|
336797
|
+
"google.protobuf.Empty",
|
|
336798
|
+
"google.protobuf.Timestamp",
|
|
336799
|
+
"google.protobuf.Duration"
|
|
336800
|
+
];
|
|
336801
336801
|
var currentDir = new URL(".", import.meta.url).pathname;
|
|
336802
336802
|
var templateDir = path.join(currentDir, "..", "templates");
|
|
336803
|
-
var
|
|
336804
|
-
|
|
336805
|
-
|
|
336806
|
-
|
|
336807
|
-
|
|
336808
|
-
|
|
336809
|
-
|
|
336810
|
-
|
|
336811
|
-
|
|
336812
|
-
const
|
|
336813
|
-
|
|
336814
|
-
|
|
336815
|
-
return true;
|
|
336816
|
-
if (field.fieldKind === "message") {
|
|
336817
|
-
if (isPaginatedDeep(field.message, visited))
|
|
336818
|
-
return true;
|
|
336819
|
-
}
|
|
336820
|
-
}
|
|
336821
|
-
return false;
|
|
336822
|
-
}
|
|
336823
|
-
function processType(typeDesc, serviceFile, wktImports, localImports, externalImports) {
|
|
336824
|
-
const fullTypeName = typeDesc.typeName;
|
|
336825
|
-
const baseName = typeDesc.name;
|
|
336826
|
-
if (KNOWN_TYPES[fullTypeName]) {
|
|
336827
|
-
wktImports.add(KNOWN_TYPES[fullTypeName]);
|
|
336828
|
-
return KNOWN_TYPES[fullTypeName];
|
|
336829
|
-
}
|
|
336830
|
-
const importPath = `./gen/${typeDesc.file.name.replace(".proto", "_pb")}`;
|
|
336831
|
-
if (typeDesc.file.name === serviceFile.name) {
|
|
336832
|
-
localImports.add(baseName);
|
|
336833
|
-
} else {
|
|
336834
|
-
if (!externalImports.has(importPath)) {
|
|
336835
|
-
externalImports.set(importPath, new Set);
|
|
336836
|
-
}
|
|
336837
|
-
externalImports.get(importPath).add(baseName);
|
|
336803
|
+
var templates = {
|
|
336804
|
+
client: fs.readFileSync(path.join(templateDir, "client.ts.mustache"), "utf-8"),
|
|
336805
|
+
api: fs.readFileSync(path.join(templateDir, "api.ts.mustache"), "utf-8"),
|
|
336806
|
+
rpc: fs.readFileSync(path.join(templateDir, "rpc.ts.mustache"), "utf-8"),
|
|
336807
|
+
index: fs.readFileSync(path.join(templateDir, "index.ts.mustache"), "utf-8")
|
|
336808
|
+
};
|
|
336809
|
+
function findPath(msg, targets, depth = 0) {
|
|
336810
|
+
if (depth > 4)
|
|
336811
|
+
return null;
|
|
336812
|
+
for (const f of msg.fields) {
|
|
336813
|
+
if (targets.includes(f.name.toLowerCase()))
|
|
336814
|
+
return [f.name];
|
|
336838
336815
|
}
|
|
336839
|
-
|
|
336840
|
-
|
|
336841
|
-
|
|
336842
|
-
|
|
336843
|
-
|
|
336844
|
-
seen.add(message.typeName);
|
|
336845
|
-
processType(message, serviceFile, wktImports, localImports, externalImports);
|
|
336846
|
-
for (const field of message.fields) {
|
|
336847
|
-
if (field.fieldKind === "message") {
|
|
336848
|
-
collectAllMessages(field.message, serviceFile, wktImports, localImports, externalImports, seen);
|
|
336816
|
+
for (const f of msg.fields) {
|
|
336817
|
+
if (f.fieldKind === "message") {
|
|
336818
|
+
const p = findPath(f.message, targets, depth + 1);
|
|
336819
|
+
if (p)
|
|
336820
|
+
return [f.name, ...p];
|
|
336849
336821
|
}
|
|
336850
336822
|
}
|
|
336823
|
+
return null;
|
|
336851
336824
|
}
|
|
336852
|
-
function processService(service
|
|
336853
|
-
const
|
|
336825
|
+
function processService(service) {
|
|
336826
|
+
const importMap = new Map;
|
|
336854
336827
|
const wktImports = new Set;
|
|
336855
|
-
const
|
|
336856
|
-
|
|
336857
|
-
|
|
336858
|
-
|
|
336859
|
-
|
|
336860
|
-
|
|
336861
|
-
|
|
336862
|
-
|
|
336863
|
-
|
|
336864
|
-
|
|
336865
|
-
|
|
336866
|
-
|
|
336867
|
-
|
|
336868
|
-
|
|
336869
|
-
functionName: camelName,
|
|
336870
|
-
hookName: `use${method.name}`,
|
|
336871
|
-
resource,
|
|
336872
|
-
inputType: method.input.name,
|
|
336873
|
-
outputType: method.output.name,
|
|
336874
|
-
isQuery: method.methodKind === METHOD_KIND_UNARY && !mutationVerbs.some((v) => method.name.startsWith(v)),
|
|
336875
|
-
isPaginated: isPaginatedDeep(method.input) && method.methodKind === METHOD_KIND_UNARY
|
|
336876
|
-
});
|
|
336828
|
+
const allMessages = new Set;
|
|
336829
|
+
function track(msg) {
|
|
336830
|
+
if (KNOWN_WKT.includes(msg.typeName)) {
|
|
336831
|
+
wktImports.add(msg.name);
|
|
336832
|
+
return;
|
|
336833
|
+
}
|
|
336834
|
+
if (allMessages.has(msg.name))
|
|
336835
|
+
return;
|
|
336836
|
+
allMessages.add(msg.name);
|
|
336837
|
+
const importPath = `./gen/${msg.file.name.replace(".proto", "_pb")}`;
|
|
336838
|
+
if (!importMap.has(importPath))
|
|
336839
|
+
importMap.set(importPath, new Set);
|
|
336840
|
+
importMap.get(importPath).add(msg.name);
|
|
336841
|
+
msg.fields.forEach((f) => f.fieldKind === "message" && track(f.message));
|
|
336877
336842
|
}
|
|
336843
|
+
const rpcs = service.methods.map((m) => {
|
|
336844
|
+
track(m.input);
|
|
336845
|
+
track(m.output);
|
|
336846
|
+
const isUnary = m.methodKind === METHOD_KIND_UNARY;
|
|
336847
|
+
const verbs = [
|
|
336848
|
+
"Create",
|
|
336849
|
+
"Update",
|
|
336850
|
+
"Delete",
|
|
336851
|
+
"Remove",
|
|
336852
|
+
"Patch",
|
|
336853
|
+
"Post",
|
|
336854
|
+
"Set",
|
|
336855
|
+
"Add"
|
|
336856
|
+
];
|
|
336857
|
+
const isMutation = verbs.some((v) => m.name.startsWith(v));
|
|
336858
|
+
const reqPath = findPath(m.input, [
|
|
336859
|
+
"page",
|
|
336860
|
+
"offset",
|
|
336861
|
+
"pagetoken",
|
|
336862
|
+
"cursor",
|
|
336863
|
+
"pagenumber"
|
|
336864
|
+
]);
|
|
336865
|
+
const resPath = findPath(m.output, [
|
|
336866
|
+
"nextpagetoken",
|
|
336867
|
+
"nextpage",
|
|
336868
|
+
"hasmore",
|
|
336869
|
+
"nextcursor"
|
|
336870
|
+
]);
|
|
336871
|
+
return {
|
|
336872
|
+
functionName: m.name.charAt(0).toLowerCase() + m.name.slice(1),
|
|
336873
|
+
hookName: `use${m.name}`,
|
|
336874
|
+
resource: m.name.replace(/^(Get|ListAll|List|Create|Update|Delete|Remove|Patch|Post|Set|Add)/, ""),
|
|
336875
|
+
inputType: m.input.name,
|
|
336876
|
+
outputType: m.output.name,
|
|
336877
|
+
isQuery: isUnary && !isMutation,
|
|
336878
|
+
isPaginated: isUnary && !isMutation && !!reqPath && !!resPath,
|
|
336879
|
+
reqPath: reqPath?.join("."),
|
|
336880
|
+
resPath: resPath?.join(".")
|
|
336881
|
+
};
|
|
336882
|
+
});
|
|
336878
336883
|
return {
|
|
336879
336884
|
serviceName: service.name,
|
|
336880
|
-
protoPbFile,
|
|
336885
|
+
protoPbFile: service.file.name.replace(".proto", "_pb"),
|
|
336881
336886
|
rpcs,
|
|
336882
|
-
messageNames: Array.from(
|
|
336887
|
+
messageNames: Array.from(allMessages),
|
|
336883
336888
|
wktImports: Array.from(wktImports),
|
|
336884
|
-
|
|
336885
|
-
externalImports: Array.from(externalImports.entries()).map(([path2, types2]) => ({
|
|
336889
|
+
externalImports: Array.from(importMap.entries()).map(([path2, types2]) => ({
|
|
336886
336890
|
path: path2,
|
|
336887
336891
|
types: Array.from(types2)
|
|
336888
336892
|
}))
|
|
@@ -336890,16 +336894,15 @@ function processService(service, protoPbFile) {
|
|
|
336890
336894
|
}
|
|
336891
336895
|
var plugin = createEcmaScriptPlugin({
|
|
336892
336896
|
name: "protoc-gen-connect-vue",
|
|
336893
|
-
version: "v1.0.
|
|
336897
|
+
version: "v1.0.7",
|
|
336894
336898
|
generateTs: (schema) => {
|
|
336895
|
-
|
|
336896
|
-
if (!
|
|
336899
|
+
const service = schema.files.flatMap((f) => f.services)[0];
|
|
336900
|
+
if (!service)
|
|
336897
336901
|
return;
|
|
336898
|
-
const
|
|
336899
|
-
|
|
336900
|
-
schema.generateFile("
|
|
336901
|
-
schema.generateFile("
|
|
336902
|
-
schema.generateFile("index.ts").print(mustache_default.render(indexTemplate, {}));
|
|
336902
|
+
const data = processService(service);
|
|
336903
|
+
schema.generateFile("client.ts").print(mustache_default.render(templates.client, data));
|
|
336904
|
+
schema.generateFile("api.ts").print(mustache_default.render(templates.api, data, { rpc: templates.rpc }));
|
|
336905
|
+
schema.generateFile("index.ts").print(mustache_default.render(templates.index, {}));
|
|
336903
336906
|
}
|
|
336904
336907
|
});
|
|
336905
336908
|
runNodeJs(plugin);
|
package/package.json
CHANGED
|
@@ -1,28 +1,16 @@
|
|
|
1
1
|
import { computed, unref } from "vue";
|
|
2
2
|
import {
|
|
3
|
-
useQuery,
|
|
4
|
-
useMutation,
|
|
5
|
-
useInfiniteQuery,
|
|
6
|
-
useQueryClient,
|
|
7
|
-
useIsFetching,
|
|
8
|
-
useIsMutating
|
|
3
|
+
useQuery, useMutation, useInfiniteQuery, useQueryClient, useIsFetching, useIsMutating
|
|
9
4
|
} from "@tanstack/vue-query";
|
|
10
5
|
import { ConnectError } from "@connectrpc/connect";
|
|
11
|
-
|
|
12
|
-
import { create, type MessageInit } from "@bufbuild/protobuf";
|
|
6
|
+
import { create } from "@bufbuild/protobuf";
|
|
13
7
|
import { useGrpcClient } from "./client";
|
|
14
8
|
|
|
15
9
|
{{#wktImports}}
|
|
16
|
-
// FIXED: Path for v2 WKTs
|
|
17
10
|
import { {{.}}Schema } from "@bufbuild/protobuf/wkt";
|
|
18
11
|
import type { {{.}} } from "@bufbuild/protobuf/wkt";
|
|
19
12
|
{{/wktImports}}
|
|
20
13
|
|
|
21
|
-
{{#localImports}}
|
|
22
|
-
import { {{.}}Schema } from "./gen/{{{protoPbFile}}}";
|
|
23
|
-
import type { {{.}} } from "./gen/{{{protoPbFile}}}";
|
|
24
|
-
{{/localImports}}
|
|
25
|
-
|
|
26
14
|
{{#externalImports}}
|
|
27
15
|
import { {{#types}}{{.}}Schema, {{/types}} } from "{{{path}}}";
|
|
28
16
|
import type { {{#types}}{{.}}, {{/types}} } from "{{{path}}}";
|
|
@@ -31,18 +19,15 @@ import type { {{#types}}{{.}}, {{/types}} } from "{{{path}}}";
|
|
|
31
19
|
export type APIResponse<T> = { error: string | null; data: T | null; };
|
|
32
20
|
|
|
33
21
|
async function callConnect<ReqT, ResT, DataT>(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
method: (req: ReqT) => Promise<ResT>,
|
|
23
|
+
req: ReqT,
|
|
24
|
+
extractor: (res: ResT) => DataT
|
|
37
25
|
): Promise<APIResponse<DataT>> {
|
|
38
26
|
try {
|
|
39
|
-
const res = await
|
|
40
|
-
return { error: null, data:
|
|
27
|
+
const res = await method(req);
|
|
28
|
+
return { error: null, data: extractor(res) };
|
|
41
29
|
} catch (err) {
|
|
42
|
-
return {
|
|
43
|
-
error: err instanceof ConnectError ? err.message : "Unknown error",
|
|
44
|
-
data: null
|
|
45
|
-
};
|
|
30
|
+
return { error: err instanceof ConnectError ? err.message : "Unknown error", data: null };
|
|
46
31
|
}
|
|
47
32
|
}
|
|
48
33
|
|
|
@@ -52,34 +37,21 @@ export const queryKeys = {
|
|
|
52
37
|
{{/rpcs}}
|
|
53
38
|
};
|
|
54
39
|
|
|
55
|
-
/**
|
|
56
|
-
* Utility to create default/empty versions of Protobuf messages
|
|
57
|
-
*/
|
|
58
40
|
export const createEmpty = {
|
|
59
41
|
{{#messageNames}}
|
|
60
|
-
{{.}}: (data?:
|
|
42
|
+
{{.}}: (data?: any) => create({{.}}Schema, data) as {{.}},
|
|
61
43
|
{{/messageNames}}
|
|
62
44
|
};
|
|
63
45
|
|
|
64
46
|
export const useApi = () => {
|
|
65
47
|
const { client } = useGrpcClient();
|
|
66
48
|
const queryClient = useQueryClient();
|
|
49
|
+
const isGlobalLoading = computed(() => useIsFetching().value > 0 || useIsMutating().value > 0);
|
|
67
50
|
|
|
68
|
-
|
|
69
|
-
const isMutating = useIsMutating();
|
|
70
|
-
const isGlobalLoading = computed(() => isFetching.value > 0 || isMutating.value > 0);
|
|
71
|
-
|
|
72
|
-
{{#rpcs}}
|
|
73
|
-
{{> rpc}}
|
|
74
|
-
{{/rpcs}}
|
|
51
|
+
{{#rpcs}}{{> rpc}}{{/rpcs}}
|
|
75
52
|
|
|
76
53
|
return {
|
|
77
|
-
{{#rpcs}}
|
|
78
|
-
|
|
79
|
-
{{hookName}},
|
|
80
|
-
{{/rpcs}}
|
|
81
|
-
isGlobalLoading,
|
|
82
|
-
queryKeys,
|
|
83
|
-
createEmpty
|
|
54
|
+
{{#rpcs}}{{functionName}}, {{hookName}}, {{/rpcs}}
|
|
55
|
+
isGlobalLoading, queryKeys, createEmpty
|
|
84
56
|
};
|
|
85
57
|
};
|
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Used for manual actions. Invalidates the cache for this resource on success.
|
|
2
|
+
* Async Wrapper: {{functionName}}
|
|
4
3
|
*/
|
|
5
4
|
const {{functionName}} = async (req: {{inputType}}): Promise<APIResponse<{{outputType}}>> => {
|
|
6
5
|
const res = await callConnect(client.value.{{functionName}}.bind(client.value), req, (res) => res);
|
|
7
|
-
if (!res.error) {
|
|
8
|
-
queryClient.invalidateQueries({ queryKey: ["{{resource}}"] });
|
|
9
|
-
}
|
|
6
|
+
if (!res.error) queryClient.invalidateQueries({ queryKey: ["{{resource}}"] });
|
|
10
7
|
return res;
|
|
11
8
|
};
|
|
12
9
|
|
|
13
10
|
/**
|
|
14
|
-
*
|
|
15
|
-
* Provides reactive data binding with caching.
|
|
11
|
+
* Hook: {{hookName}}
|
|
16
12
|
*/
|
|
17
13
|
const {{hookName}} = (
|
|
18
|
-
{{#isQuery}}
|
|
19
|
-
|
|
20
|
-
options: any = {}
|
|
21
|
-
{{/isQuery}}
|
|
22
|
-
{{^isQuery}}
|
|
23
|
-
options: any = {}
|
|
24
|
-
{{/isQuery}}
|
|
14
|
+
{{#isQuery}}input: any, options: any = {}{{/isQuery}}
|
|
15
|
+
{{^isQuery}}options: any = {}{{/isQuery}}
|
|
25
16
|
) => {
|
|
26
17
|
{{#isPaginated}}
|
|
27
18
|
return useInfiniteQuery({
|
|
28
19
|
queryKey: queryKeys.{{functionName}}(input),
|
|
29
20
|
queryFn: async ({ pageParam }) => {
|
|
30
|
-
const req =
|
|
21
|
+
const req = JSON.parse(JSON.stringify(unref(input)));
|
|
22
|
+
const path = "{{reqPath}}".split('.');
|
|
23
|
+
let curr = req;
|
|
24
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
25
|
+
curr[path[i]] = curr[path[i]] || {};
|
|
26
|
+
curr = curr[path[i]];
|
|
27
|
+
}
|
|
28
|
+
curr[path[path.length - 1]] = pageParam;
|
|
31
29
|
return client.value.{{functionName}}(req);
|
|
32
30
|
},
|
|
33
|
-
initialPageParam: 1,
|
|
34
|
-
getNextPageParam: (lastPage: any) =>
|
|
31
|
+
initialPageParam: options.initialPageParam ?? 1,
|
|
32
|
+
getNextPageParam: (lastPage: any) => {
|
|
33
|
+
const path = "{{resPath}}".split('.');
|
|
34
|
+
return path.reduce((o, i) => o?.[i], lastPage) || undefined;
|
|
35
|
+
},
|
|
35
36
|
...options,
|
|
36
37
|
});
|
|
37
38
|
{{/isPaginated}}
|
|
38
|
-
|
|
39
39
|
{{^isPaginated}}
|
|
40
40
|
{{#isQuery}}
|
|
41
41
|
return useQuery({
|
|
@@ -44,13 +44,12 @@ const {{hookName}} = (
|
|
|
44
44
|
...options,
|
|
45
45
|
});
|
|
46
46
|
{{/isQuery}}
|
|
47
|
-
|
|
48
47
|
{{^isQuery}}
|
|
49
48
|
return useMutation({
|
|
50
49
|
mutationFn: (req: {{inputType}}) => client.value.{{functionName}}(req),
|
|
51
|
-
onSuccess: async (
|
|
50
|
+
onSuccess: async (d, v, c) => {
|
|
52
51
|
await queryClient.invalidateQueries({ queryKey: ["{{resource}}"] });
|
|
53
|
-
if (options.onSuccess) return options.onSuccess(
|
|
52
|
+
if (options.onSuccess) return options.onSuccess(d, v, c);
|
|
54
53
|
},
|
|
55
54
|
...options,
|
|
56
55
|
});
|