@zachacious/protoc-gen-connect-vue 1.0.9 → 1.0.11
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 +79 -79
- package/package.json +1 -1
- package/templates/api.ts.mustache +9 -18
- package/templates/rpc.ts.mustache +21 -8
package/dist/generator.js
CHANGED
|
@@ -336793,69 +336793,59 @@ 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
|
-
"cursor",
|
|
336816
|
-
"limit",
|
|
336817
|
-
"pagesize",
|
|
336818
|
-
"pagenumber"
|
|
336819
|
-
];
|
|
336820
|
-
for (const field of message.fields) {
|
|
336821
|
-
if (pagingKeys.includes(field.name.toLowerCase()))
|
|
336822
|
-
return true;
|
|
336823
|
-
if (field.fieldKind === "message" && isPaginatedDeep(field.message, visited))
|
|
336824
|
-
return true;
|
|
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];
|
|
336825
336815
|
}
|
|
336826
|
-
|
|
336827
|
-
|
|
336828
|
-
|
|
336829
|
-
|
|
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];
|
|
336821
|
+
}
|
|
336822
|
+
}
|
|
336823
|
+
return null;
|
|
336830
336824
|
}
|
|
336831
336825
|
function processService(service) {
|
|
336832
|
-
const rpcs = [];
|
|
336833
|
-
const wktImports = new Set;
|
|
336834
336826
|
const importMap = new Map;
|
|
336835
|
-
const
|
|
336836
|
-
|
|
336837
|
-
|
|
336838
|
-
|
|
336839
|
-
|
|
336840
|
-
if (KNOWN_TYPES[fullTypeName]) {
|
|
336841
|
-
wktImports.add(KNOWN_TYPES[fullTypeName]);
|
|
336842
|
-
allMessages.set(KNOWN_TYPES[fullTypeName], "@bufbuild/protobuf/wkt");
|
|
336827
|
+
const wktImports = new Set;
|
|
336828
|
+
const allMessages = new Set;
|
|
336829
|
+
function track(msg) {
|
|
336830
|
+
if (KNOWN_WKT.includes(msg.typeName)) {
|
|
336831
|
+
wktImports.add(msg.name);
|
|
336843
336832
|
return;
|
|
336844
336833
|
}
|
|
336845
|
-
|
|
336846
|
-
|
|
336847
|
-
|
|
336848
|
-
|
|
336849
|
-
|
|
336850
|
-
|
|
336851
|
-
|
|
336852
|
-
|
|
336853
|
-
|
|
336834
|
+
if (allMessages.has(msg.name))
|
|
336835
|
+
return;
|
|
336836
|
+
allMessages.add(msg.name);
|
|
336837
|
+
const baseFileName = msg.file.name.replace(".proto", "");
|
|
336838
|
+
const importPath = `./gen/${baseFileName}_pb`;
|
|
336839
|
+
if (!importMap.has(importPath))
|
|
336840
|
+
importMap.set(importPath, new Set);
|
|
336841
|
+
importMap.get(importPath).add(msg.name);
|
|
336842
|
+
msg.fields.forEach((f) => f.fieldKind === "message" && track(f.message));
|
|
336854
336843
|
}
|
|
336855
|
-
|
|
336856
|
-
|
|
336857
|
-
|
|
336858
|
-
const
|
|
336844
|
+
const rpcs = service.methods.map((m) => {
|
|
336845
|
+
track(m.input);
|
|
336846
|
+
track(m.output);
|
|
336847
|
+
const isUnary = m.methodKind === METHOD_KIND_UNARY;
|
|
336848
|
+
const verbs = [
|
|
336859
336849
|
"Create",
|
|
336860
336850
|
"Update",
|
|
336861
336851
|
"Delete",
|
|
@@ -336865,27 +336855,37 @@ function processService(service) {
|
|
|
336865
336855
|
"Set",
|
|
336866
336856
|
"Add"
|
|
336867
336857
|
];
|
|
336868
|
-
const
|
|
336869
|
-
|
|
336870
|
-
|
|
336871
|
-
|
|
336872
|
-
|
|
336873
|
-
|
|
336874
|
-
|
|
336875
|
-
|
|
336876
|
-
|
|
336877
|
-
|
|
336878
|
-
|
|
336879
|
-
|
|
336880
|
-
|
|
336881
|
-
|
|
336882
|
-
|
|
336883
|
-
|
|
336858
|
+
const isMutation = verbs.some((v) => m.name.startsWith(v));
|
|
336859
|
+
const reqPath = findPath(m.input, [
|
|
336860
|
+
"page",
|
|
336861
|
+
"offset",
|
|
336862
|
+
"pagetoken",
|
|
336863
|
+
"cursor",
|
|
336864
|
+
"pagenumber"
|
|
336865
|
+
]);
|
|
336866
|
+
const resPath = findPath(m.output, [
|
|
336867
|
+
"nextpagetoken",
|
|
336868
|
+
"nextpage",
|
|
336869
|
+
"hasmore",
|
|
336870
|
+
"nextcursor"
|
|
336871
|
+
]);
|
|
336872
|
+
return {
|
|
336873
|
+
functionName: m.name.charAt(0).toLowerCase() + m.name.slice(1),
|
|
336874
|
+
hookName: `use${m.name}`,
|
|
336875
|
+
resource: m.name.replace(/^(Get|ListAll|List|Create|Update|Delete|Remove|Patch|Post|Set|Add)/, "") || "Global",
|
|
336876
|
+
inputType: m.input.name,
|
|
336877
|
+
outputType: m.output.name,
|
|
336878
|
+
isQuery: isUnary && !isMutation,
|
|
336879
|
+
isPaginated: isUnary && !isMutation && !!reqPath && !!resPath,
|
|
336880
|
+
reqPath: reqPath?.join("."),
|
|
336881
|
+
resPath: resPath?.join(".")
|
|
336882
|
+
};
|
|
336883
|
+
});
|
|
336884
336884
|
return {
|
|
336885
336885
|
serviceName: service.name,
|
|
336886
|
-
protoPbFile: service.file.name.replace(".proto", "
|
|
336886
|
+
protoPbFile: `${service.file.name.replace(".proto", "")}_pb`,
|
|
336887
336887
|
rpcs,
|
|
336888
|
-
messageNames: Array.from(allMessages
|
|
336888
|
+
messageNames: Array.from(allMessages),
|
|
336889
336889
|
wktImports: Array.from(wktImports),
|
|
336890
336890
|
externalImports: Array.from(importMap.entries()).map(([path2, types2]) => ({
|
|
336891
336891
|
path: path2,
|
|
@@ -336895,15 +336895,15 @@ function processService(service) {
|
|
|
336895
336895
|
}
|
|
336896
336896
|
var plugin = createEcmaScriptPlugin({
|
|
336897
336897
|
name: "protoc-gen-connect-vue",
|
|
336898
|
-
version: "v1.0
|
|
336898
|
+
version: "v1.1.0",
|
|
336899
336899
|
generateTs: (schema) => {
|
|
336900
|
-
const
|
|
336901
|
-
if (!
|
|
336900
|
+
const service = schema.files.flatMap((f) => f.services)[0];
|
|
336901
|
+
if (!service)
|
|
336902
336902
|
return;
|
|
336903
|
-
const
|
|
336904
|
-
schema.generateFile("client.ts").print(mustache_default.render(
|
|
336905
|
-
schema.generateFile("api.ts").print(mustache_default.render(
|
|
336906
|
-
schema.generateFile("index.ts").print(mustache_default.render(
|
|
336903
|
+
const data = processService(service);
|
|
336904
|
+
schema.generateFile("client.ts").print(mustache_default.render(templates.client, data));
|
|
336905
|
+
schema.generateFile("api.ts").print(mustache_default.render(templates.api, data, { rpc: templates.rpc }));
|
|
336906
|
+
schema.generateFile("index.ts").print(mustache_default.render(templates.index, {}));
|
|
336907
336907
|
}
|
|
336908
336908
|
});
|
|
336909
336909
|
runNodeJs(plugin);
|
package/package.json
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
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
|
-
import { create
|
|
6
|
+
import { create } from "@bufbuild/protobuf";
|
|
12
7
|
import { useGrpcClient } from "./client";
|
|
13
8
|
|
|
14
9
|
{{#wktImports}}
|
|
@@ -24,18 +19,15 @@ import type { {{#types}}{{.}}, {{/types}} } from "{{{path}}}";
|
|
|
24
19
|
export type APIResponse<T> = { error: string | null; data: T | null; };
|
|
25
20
|
|
|
26
21
|
async function callConnect<ReqT, ResT, DataT>(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
method: (req: ReqT) => Promise<ResT>,
|
|
23
|
+
req: ReqT,
|
|
24
|
+
extractor: (res: ResT) => DataT
|
|
30
25
|
): Promise<APIResponse<DataT>> {
|
|
31
26
|
try {
|
|
32
|
-
const res = await
|
|
33
|
-
return { error: null, data:
|
|
27
|
+
const res = await method(req);
|
|
28
|
+
return { error: null, data: extractor(res) };
|
|
34
29
|
} catch (err) {
|
|
35
|
-
return {
|
|
36
|
-
error: err instanceof ConnectError ? err.message : "Unknown error",
|
|
37
|
-
data: null
|
|
38
|
-
};
|
|
30
|
+
return { error: err instanceof ConnectError ? err.message : "Unknown error", data: null };
|
|
39
31
|
}
|
|
40
32
|
}
|
|
41
33
|
|
|
@@ -47,14 +39,13 @@ export const queryKeys = {
|
|
|
47
39
|
|
|
48
40
|
export const createEmpty = {
|
|
49
41
|
{{#messageNames}}
|
|
50
|
-
{{.}}: (data?:
|
|
42
|
+
{{.}}: (data?: any) => create({{.}}Schema, data) as {{.}},
|
|
51
43
|
{{/messageNames}}
|
|
52
44
|
};
|
|
53
45
|
|
|
54
46
|
export const useApi = () => {
|
|
55
47
|
const { client } = useGrpcClient();
|
|
56
48
|
const queryClient = useQueryClient();
|
|
57
|
-
|
|
58
49
|
const isFetching = useIsFetching();
|
|
59
50
|
const isMutating = useIsMutating();
|
|
60
51
|
const isGlobalLoading = computed(() => isFetching.value > 0 || isMutating.value > 0);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Used for manual actions. Invalidates the cache for this resource on success.
|
|
2
|
+
* Async: {{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);
|
|
@@ -11,12 +10,11 @@ const {{functionName}} = async (req: {{inputType}}): Promise<APIResponse<{{outpu
|
|
|
11
10
|
};
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
|
-
*
|
|
15
|
-
* Provides reactive data binding with caching.
|
|
13
|
+
* Hook: {{hookName}}
|
|
16
14
|
*/
|
|
17
15
|
const {{hookName}} = (
|
|
18
16
|
{{#isQuery}}
|
|
19
|
-
input: any,
|
|
17
|
+
input: any,
|
|
20
18
|
options: any = {}
|
|
21
19
|
{{/isQuery}}
|
|
22
20
|
{{^isQuery}}
|
|
@@ -27,11 +25,26 @@ const {{hookName}} = (
|
|
|
27
25
|
return useInfiniteQuery({
|
|
28
26
|
queryKey: queryKeys.{{functionName}}(input),
|
|
29
27
|
queryFn: async ({ pageParam }) => {
|
|
30
|
-
|
|
28
|
+
// Deep clone to avoid mutating reactive state
|
|
29
|
+
const req = JSON.parse(JSON.stringify(unref(input)));
|
|
30
|
+
|
|
31
|
+
// Structural Injection: Navigate to the field found during generation
|
|
32
|
+
const path = "{{reqPath}}".split('.');
|
|
33
|
+
let current = req;
|
|
34
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
35
|
+
if (!current[path[i]]) current[path[i]] = {};
|
|
36
|
+
current = current[path[i]];
|
|
37
|
+
}
|
|
38
|
+
current[path[path.length - 1]] = pageParam;
|
|
39
|
+
|
|
31
40
|
return client.value.{{functionName}}(req);
|
|
32
41
|
},
|
|
33
|
-
initialPageParam: 1,
|
|
34
|
-
getNextPageParam: (lastPage: any) =>
|
|
42
|
+
initialPageParam: options.initialPageParam ?? 1,
|
|
43
|
+
getNextPageParam: (lastPage: any) => {
|
|
44
|
+
// Structural Extraction: Navigate to the field found during generation
|
|
45
|
+
const path = "{{resPath}}".split('.');
|
|
46
|
+
return path.reduce((o, i) => o?.[i], lastPage) || undefined;
|
|
47
|
+
},
|
|
35
48
|
...options,
|
|
36
49
|
});
|
|
37
50
|
{{/isPaginated}}
|