@lewebsimple/nuxt-graphql 0.1.6 → 0.1.8
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/README.md +23 -1
- package/dist/module.d.mts +7 -5
- package/dist/module.json +1 -1
- package/dist/module.mjs +267 -21
- package/dist/runtime/composables/useGraphQL.d.ts +2 -2
- package/dist/runtime/composables/useGraphQL.js +4 -3
- package/dist/runtime/composables/useGraphQLMutation.d.ts +10 -0
- package/dist/runtime/composables/useGraphQLMutation.js +25 -0
- package/dist/runtime/composables/useGraphQLQuery.d.ts +5 -0
- package/dist/runtime/composables/useGraphQLQuery.js +11 -0
- package/dist/types.d.mts +1 -1
- package/package.json +16 -3
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ Opinionated Nuxt module for using GraphQL Yoga with graphql-request / graphql-ss
|
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
- 🧘♂️ GraphQL Yoga server handler with user-provided schema / context
|
|
15
|
+
- 📄 Auto-import GraphQL documents from `**/*.gql` (configurable)
|
|
16
|
+
- 🧩 Type-safe composables to call operations by name, i.e. `useGraphQLQuery("Hello")`
|
|
15
17
|
|
|
16
18
|
## Quick Setup
|
|
17
19
|
|
|
@@ -21,6 +23,25 @@ Install the module to your Nuxt application with one command:
|
|
|
21
23
|
npx nuxi module add @lewebsimple/nuxt-graphql
|
|
22
24
|
```
|
|
23
25
|
|
|
26
|
+
Optionnally adjust options in your Nuxt config. The defaults shown below:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
// nuxt.config.ts
|
|
30
|
+
export default defineNuxtConfig({
|
|
31
|
+
modules: ["@lewebsimple/nuxt-graphql"],
|
|
32
|
+
graphql: {
|
|
33
|
+
// GraphQL HTTP endpoint served by Yoga
|
|
34
|
+
endpoint: "/api/graphql",
|
|
35
|
+
// Codegen controls document scanning and outputs
|
|
36
|
+
codegen: {
|
|
37
|
+
enabled: true,
|
|
38
|
+
pattern: "**/*.gql", // scan .gql files across layers
|
|
39
|
+
schemaOutput: "server/graphql/schema.graphql", // saved SDL
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
24
45
|
Define your GraphQL schema in `server/graphql/schema.ts`:
|
|
25
46
|
|
|
26
47
|
```ts
|
|
@@ -41,7 +62,7 @@ export const schema = createSchema<GraphQLContext>({
|
|
|
41
62
|
});
|
|
42
63
|
```
|
|
43
64
|
|
|
44
|
-
|
|
65
|
+
Optionnally define your GraphQL context in `server/graphql/context.ts`:
|
|
45
66
|
|
|
46
67
|
```ts
|
|
47
68
|
import type { H3Event } from "h3";
|
|
@@ -57,6 +78,7 @@ export type GraphQLContext = Awaited<ReturnType<typeof createContext>>;
|
|
|
57
78
|
|
|
58
79
|
That's it! You can now use Nuxt GraphQL in your Nuxt app ✨
|
|
59
80
|
|
|
81
|
+
Yoga GraphiQL is available at `http://localhost:3000/api/graphql` by default.
|
|
60
82
|
|
|
61
83
|
## Contribution
|
|
62
84
|
|
package/dist/module.d.mts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
|
|
3
|
-
interface GraphQLYogaConfig {
|
|
4
|
-
endpoint?: string;
|
|
5
|
-
}
|
|
6
3
|
interface ModuleOptions {
|
|
7
|
-
|
|
4
|
+
endpoint?: string;
|
|
5
|
+
codegen?: {
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
pattern?: string;
|
|
8
|
+
schemaOutput?: string;
|
|
9
|
+
};
|
|
8
10
|
}
|
|
9
11
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
10
12
|
|
|
11
13
|
export { _default as default };
|
|
12
|
-
export type {
|
|
14
|
+
export type { ModuleOptions };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,23 +1,213 @@
|
|
|
1
1
|
import { useLogger, defineNuxtModule, createResolver, getLayerDirectories, addServerTemplate, addServerHandler, addImportsDir, addPlugin, addTypeTemplate } from '@nuxt/kit';
|
|
2
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
2
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join, dirname, relative } from 'node:path';
|
|
4
|
+
import { glob } from 'tinyglobby';
|
|
5
|
+
import { generate } from '@graphql-codegen/cli';
|
|
6
|
+
import { parse, Kind } from 'graphql';
|
|
4
7
|
|
|
5
8
|
const logger = useLogger("@lewebsimple/nuxt-graphql");
|
|
9
|
+
const blue = "\x1B[34m";
|
|
6
10
|
const cyan = "\x1B[36m";
|
|
11
|
+
const dim = "\x1B[2m";
|
|
12
|
+
const green = "\x1B[32m";
|
|
13
|
+
const magenta = "\x1B[35m";
|
|
7
14
|
const reset = "\x1B[0m";
|
|
15
|
+
const yellow = "\x1B[33m";
|
|
8
16
|
|
|
9
|
-
function
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return fullPath;
|
|
17
|
+
async function findSingleFile(dirs, pattern, isRequired = false) {
|
|
18
|
+
for (const dir of dirs) {
|
|
19
|
+
const fullPattern = join(dir, pattern);
|
|
20
|
+
const files = await glob(fullPattern, { absolute: true });
|
|
21
|
+
if (files.length > 0) {
|
|
22
|
+
return files[0];
|
|
16
23
|
}
|
|
17
24
|
}
|
|
18
25
|
if (isRequired) {
|
|
19
|
-
throw new Error(`
|
|
20
|
-
${
|
|
26
|
+
throw new Error(`File not found: ${cyan}${pattern}${reset} in directories:
|
|
27
|
+
${dirs.join("\n")}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function findMultipleFiles(dirs, pattern) {
|
|
31
|
+
const files = [];
|
|
32
|
+
for (const dir of dirs) {
|
|
33
|
+
const foundFiles = await glob(pattern, { cwd: dir, absolute: true });
|
|
34
|
+
files.push(...foundFiles);
|
|
35
|
+
}
|
|
36
|
+
return Array.from(new Set(files));
|
|
37
|
+
}
|
|
38
|
+
function writeFileIfChanged(path, content) {
|
|
39
|
+
if (existsSync(path) && readFileSync(path, "utf-8") === content) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
43
|
+
writeFileSync(path, content, "utf-8");
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function analyzeGraphQLDocuments(docs) {
|
|
48
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
49
|
+
const operationsByType = {
|
|
50
|
+
query: [],
|
|
51
|
+
mutation: [],
|
|
52
|
+
subscription: []
|
|
53
|
+
};
|
|
54
|
+
const operationNameToFile = /* @__PURE__ */ new Map();
|
|
55
|
+
const fragmentNameToFile = /* @__PURE__ */ new Map();
|
|
56
|
+
for (const doc of docs) {
|
|
57
|
+
const ast = parse(doc.content);
|
|
58
|
+
const defs = [];
|
|
59
|
+
for (const def of ast.definitions) {
|
|
60
|
+
if (def.kind === Kind.FRAGMENT_DEFINITION) {
|
|
61
|
+
const name2 = def.name.value;
|
|
62
|
+
const prev2 = fragmentNameToFile.get(name2);
|
|
63
|
+
if (prev2) {
|
|
64
|
+
throw new Error(`Duplicate fragment name '${name2}' in:
|
|
65
|
+
- ${prev2}
|
|
66
|
+
- ${doc.path}`);
|
|
67
|
+
}
|
|
68
|
+
fragmentNameToFile.set(name2, doc.path);
|
|
69
|
+
defs.push({ kind: "fragment", name: name2 });
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (def.kind !== Kind.OPERATION_DEFINITION) continue;
|
|
73
|
+
const type = def.operation;
|
|
74
|
+
if (!["query", "mutation", "subscription"].includes(type)) continue;
|
|
75
|
+
const name = def.name?.value;
|
|
76
|
+
if (!name) {
|
|
77
|
+
throw new Error(`Unnamed ${type} operation in ${doc.path}`);
|
|
78
|
+
}
|
|
79
|
+
const prev = operationNameToFile.get(name);
|
|
80
|
+
if (prev) {
|
|
81
|
+
throw new Error(`Duplicate ${type} operation name '${name}' in:
|
|
82
|
+
- ${prev}
|
|
83
|
+
- ${doc.path}`);
|
|
84
|
+
}
|
|
85
|
+
operationNameToFile.set(name, doc.path);
|
|
86
|
+
const op = { kind: "operation", type, name };
|
|
87
|
+
defs.push(op);
|
|
88
|
+
operationsByType[type].push(op);
|
|
89
|
+
}
|
|
90
|
+
byFile.set(doc.path, defs);
|
|
91
|
+
}
|
|
92
|
+
return { byFile, operationsByType };
|
|
93
|
+
}
|
|
94
|
+
function generateRegistryByTypeSource(analysis) {
|
|
95
|
+
const q = analysis.query.map((o) => o.name);
|
|
96
|
+
const m = analysis.mutation.map((o) => o.name);
|
|
97
|
+
const s = analysis.subscription.map((o) => o.name);
|
|
98
|
+
const lines = [
|
|
99
|
+
`import type { TypedDocumentNode } from "@graphql-typed-document-node/core";`,
|
|
100
|
+
`import * as ops from "#graphql/operations";`,
|
|
101
|
+
``,
|
|
102
|
+
`type ResultOf<T> = T extends { __apiType?: (variables: infer _) => infer R } ? R : never;`,
|
|
103
|
+
`type VariablesOf<T> = T extends { __apiType?: (variables: infer V) => infer _ } ? V : never;`
|
|
104
|
+
];
|
|
105
|
+
if (q.length > 0) {
|
|
106
|
+
lines.push(
|
|
107
|
+
``,
|
|
108
|
+
`export const queries = {`,
|
|
109
|
+
...q.map((name) => ` ${name}: ops.${name}Document,`),
|
|
110
|
+
`} as const;`
|
|
111
|
+
);
|
|
112
|
+
} else {
|
|
113
|
+
lines.push(``, `export const queries = {} as const;`);
|
|
114
|
+
}
|
|
115
|
+
lines.push(
|
|
116
|
+
`export type QueryName = keyof typeof queries;`,
|
|
117
|
+
`export type QueryResult<N extends QueryName> = ResultOf<(typeof queries)[N]>;`,
|
|
118
|
+
`export type QueryVariables<N extends QueryName> = VariablesOf<(typeof queries)[N]>;`
|
|
119
|
+
);
|
|
120
|
+
if (m.length > 0) {
|
|
121
|
+
lines.push(
|
|
122
|
+
``,
|
|
123
|
+
`export const mutations = {`,
|
|
124
|
+
...m.map((name) => ` ${name}: ops.${name}Document,`),
|
|
125
|
+
`} as const;`
|
|
126
|
+
);
|
|
127
|
+
} else {
|
|
128
|
+
lines.push(``, `export const mutations = {} as const;`);
|
|
129
|
+
}
|
|
130
|
+
lines.push(
|
|
131
|
+
`export type MutationName = keyof typeof mutations;`,
|
|
132
|
+
`export type MutationResult<N extends MutationName> = ResultOf<(typeof mutations)[N]>;`,
|
|
133
|
+
`export type MutationVariables<N extends MutationName> = VariablesOf<(typeof mutations)[N]>;`
|
|
134
|
+
);
|
|
135
|
+
if (s.length > 0) {
|
|
136
|
+
lines.push(
|
|
137
|
+
``,
|
|
138
|
+
`export const subscriptions = {`,
|
|
139
|
+
...s.map((name) => ` ${name}: ops.${name}Document,`),
|
|
140
|
+
`} as const;`
|
|
141
|
+
);
|
|
142
|
+
} else {
|
|
143
|
+
lines.push(``, `export const subscriptions = {} as const;`);
|
|
144
|
+
}
|
|
145
|
+
lines.push(
|
|
146
|
+
`export type SubscriptionName = keyof typeof subscriptions;`,
|
|
147
|
+
`export type SubscriptionResult<N extends SubscriptionName> = ResultOf<(typeof subscriptions)[N]>;`,
|
|
148
|
+
`export type SubscriptionVariables<N extends SubscriptionName> = VariablesOf<(typeof subscriptions)[N]>;`
|
|
149
|
+
);
|
|
150
|
+
return lines.join("\n") + "\n";
|
|
151
|
+
}
|
|
152
|
+
function formatDefinitions(defs) {
|
|
153
|
+
if (defs.length === 0) return "";
|
|
154
|
+
const colorOf = (def) => {
|
|
155
|
+
if (def.kind === "fragment") return green;
|
|
156
|
+
switch (def.type) {
|
|
157
|
+
case "query":
|
|
158
|
+
return blue;
|
|
159
|
+
case "mutation":
|
|
160
|
+
return magenta;
|
|
161
|
+
case "subscription":
|
|
162
|
+
return yellow;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
return defs.map((def) => `${colorOf(def)}${def.name}${reset}`).join(`${dim} / ${reset}`);
|
|
166
|
+
}
|
|
167
|
+
async function loadGraphQLSchema(schemaPath) {
|
|
168
|
+
const { createJiti } = await import('jiti');
|
|
169
|
+
const jiti = createJiti(import.meta.url, { interopDefault: true });
|
|
170
|
+
const module = await jiti.import(schemaPath);
|
|
171
|
+
if (!module.schema) {
|
|
172
|
+
throw new Error(`${schemaPath} must export a 'schema' variable`);
|
|
173
|
+
}
|
|
174
|
+
const { printSchema, lexicographicSortSchema } = await import('graphql');
|
|
175
|
+
return printSchema(lexicographicSortSchema(module.schema));
|
|
176
|
+
}
|
|
177
|
+
async function runCodegen(options) {
|
|
178
|
+
const { sdl, documents, operationsFile } = options;
|
|
179
|
+
if (documents.length === 0) {
|
|
180
|
+
logger.warn("No GraphQL documents found");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
await generate(
|
|
185
|
+
{
|
|
186
|
+
schema: sdl,
|
|
187
|
+
documents,
|
|
188
|
+
generates: {
|
|
189
|
+
[operationsFile]: {
|
|
190
|
+
plugins: ["typescript", "typescript-operations", "typed-document-node"],
|
|
191
|
+
config: {
|
|
192
|
+
useTypeImports: true,
|
|
193
|
+
enumsAsTypes: true,
|
|
194
|
+
skipTypename: true,
|
|
195
|
+
documentVariableSuffix: "Document",
|
|
196
|
+
documentMode: "documentNode",
|
|
197
|
+
strictScalars: true,
|
|
198
|
+
defaultScalarType: "never"
|
|
199
|
+
// TODO: Make codegen config customizable, e.g. for custom scalars
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
silent: true,
|
|
204
|
+
errorsOnly: true
|
|
205
|
+
},
|
|
206
|
+
true
|
|
207
|
+
);
|
|
208
|
+
logger.success(`Generated types for ${documents.length} document(s)`);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
logger.error("GraphQL codegen failed:", error instanceof Error ? error.message : error);
|
|
21
211
|
}
|
|
22
212
|
}
|
|
23
213
|
|
|
@@ -27,29 +217,85 @@ const module$1 = defineNuxtModule({
|
|
|
27
217
|
configKey: "graphql"
|
|
28
218
|
},
|
|
29
219
|
defaults: {
|
|
30
|
-
|
|
31
|
-
|
|
220
|
+
endpoint: "/api/graphql",
|
|
221
|
+
codegen: {
|
|
222
|
+
enabled: true,
|
|
223
|
+
pattern: "**/*.gql",
|
|
224
|
+
schemaOutput: "server/graphql/schema.graphql"
|
|
32
225
|
}
|
|
33
226
|
},
|
|
34
227
|
async setup(options, nuxt) {
|
|
35
228
|
const { resolve } = createResolver(import.meta.url);
|
|
36
229
|
const { rootDir, serverDir } = nuxt.options;
|
|
37
|
-
const layerDirs = [
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
230
|
+
const layerDirs = [
|
|
231
|
+
...getLayerDirectories(nuxt),
|
|
232
|
+
{ root: rootDir, server: serverDir.replace(rootDir, `${rootDir}/playground`) }
|
|
233
|
+
];
|
|
234
|
+
const layerServerDirs = layerDirs.map(({ server }) => server);
|
|
235
|
+
const layerRootDirs = layerDirs.map(({ root }) => root);
|
|
236
|
+
const schemaPath = await findSingleFile(layerServerDirs, "graphql/schema.{ts,mjs}", true);
|
|
237
|
+
const contextPath = await findSingleFile(layerServerDirs, "graphql/context.{ts,mjs}") || resolve("./runtime/server/default-context.ts");
|
|
238
|
+
nuxt.hook("nitro:config", (config) => {
|
|
239
|
+
config.alias ||= {};
|
|
240
|
+
config.alias["#graphql/schema"] = schemaPath;
|
|
241
|
+
config.alias["#graphql/context"] = contextPath;
|
|
42
242
|
});
|
|
43
|
-
const endpoint = options.
|
|
243
|
+
const endpoint = options.endpoint ?? "/api/graphql";
|
|
44
244
|
addServerTemplate({
|
|
45
245
|
filename: "graphql/yoga-handler",
|
|
46
246
|
getContents: () => readFileSync(resolve("./templates/yoga-handler.mjs"), "utf-8").replace("{{endpoint}}", endpoint)
|
|
47
247
|
});
|
|
48
248
|
addServerHandler({ route: endpoint, handler: "graphql/yoga-handler" });
|
|
49
|
-
nuxt.hook("listen", (
|
|
50
|
-
logger.success(`GraphQL Yoga
|
|
249
|
+
nuxt.hook("listen", (_, { url }) => {
|
|
250
|
+
logger.success(`GraphQL Yoga ready at ${cyan}${url.replace(/\/$/, "")}${endpoint}${reset}`);
|
|
51
251
|
});
|
|
52
252
|
nuxt.options.runtimeConfig.public.graphql = { endpoint };
|
|
253
|
+
if (options.codegen?.enabled !== false) {
|
|
254
|
+
const codegenPattern = options.codegen?.pattern ?? "**/*.gql";
|
|
255
|
+
const graphqlrcFile = join(rootDir, ".graphqlrc");
|
|
256
|
+
const operationsFile = join(nuxt.options.buildDir, "graphql/operations.ts");
|
|
257
|
+
const registryFile = join(nuxt.options.buildDir, "graphql/registry.ts");
|
|
258
|
+
const schemaOutput = options.codegen?.schemaOutput ?? "server/graphql/schema.graphql";
|
|
259
|
+
const schemaFile = join(rootDir, schemaOutput);
|
|
260
|
+
const generate = async () => {
|
|
261
|
+
const [sdl, documents] = await Promise.all([
|
|
262
|
+
loadGraphQLSchema(schemaPath),
|
|
263
|
+
findMultipleFiles(layerRootDirs, codegenPattern)
|
|
264
|
+
]);
|
|
265
|
+
const docs = documents.map((document) => ({ path: document, content: readFileSync(document, "utf-8") }));
|
|
266
|
+
const analysis = analyzeGraphQLDocuments(docs);
|
|
267
|
+
for (const doc of docs) {
|
|
268
|
+
const relativePath = doc.path.startsWith(rootDir) ? doc.path.slice(rootDir.length + 1) : doc.path;
|
|
269
|
+
const defs = analysis.byFile.get(doc.path) ?? [];
|
|
270
|
+
logger.info(`${cyan}${relativePath}${reset} [${formatDefinitions(defs)}]`);
|
|
271
|
+
}
|
|
272
|
+
await runCodegen({ sdl, documents, operationsFile });
|
|
273
|
+
if (writeFileIfChanged(schemaFile, sdl)) {
|
|
274
|
+
logger.info(`GraphQL schema saved to ${cyan}${schemaOutput}${reset}`);
|
|
275
|
+
}
|
|
276
|
+
const config = JSON.stringify({ schema: relative(rootDir, schemaFile), documents: codegenPattern }, null, 2);
|
|
277
|
+
if (writeFileIfChanged(graphqlrcFile, config)) {
|
|
278
|
+
logger.info(`GraphQL config saved to ${cyan}.graphqlrc${reset}`);
|
|
279
|
+
}
|
|
280
|
+
if (writeFileIfChanged(registryFile, generateRegistryByTypeSource(analysis.operationsByType))) {
|
|
281
|
+
logger.info(`GraphQL registry saved to ${cyan}${relative(rootDir, registryFile)}${reset}`);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
nuxt.hook("prepare:types", async ({ references }) => {
|
|
285
|
+
await generate();
|
|
286
|
+
if (existsSync(operationsFile)) references.push({ path: operationsFile });
|
|
287
|
+
if (existsSync(registryFile)) references.push({ path: registryFile });
|
|
288
|
+
});
|
|
289
|
+
if (nuxt.options.dev) {
|
|
290
|
+
nuxt.hook("builder:watch", async (event, path) => {
|
|
291
|
+
if (path.endsWith(".gql")) {
|
|
292
|
+
await generate();
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
nuxt.options.alias["#graphql/operations"] = operationsFile;
|
|
297
|
+
nuxt.options.alias["#graphql/registry"] = registryFile;
|
|
298
|
+
}
|
|
53
299
|
addImportsDir(resolve("./runtime/composables"));
|
|
54
300
|
addPlugin(resolve("./runtime/plugins/graphql"));
|
|
55
301
|
addTypeTemplate({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TypedDocumentNode } from "@graphql-typed-document-node/core";
|
|
2
2
|
export declare function useGraphQL(): {
|
|
3
|
-
request: <
|
|
3
|
+
request: <TResult, TVariables extends Record<string, unknown> | undefined>(document: TypedDocumentNode<TResult, TVariables>, variables?: TVariables) => Promise<TResult>;
|
|
4
4
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useNuxtApp } from "#imports";
|
|
2
2
|
export function useGraphQL() {
|
|
3
3
|
const { $graphql } = useNuxtApp();
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
4
|
+
async function request(document, variables) {
|
|
5
|
+
return $graphql.request({ document, variables });
|
|
6
|
+
}
|
|
7
|
+
return { request };
|
|
7
8
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
import { type MutationName, type MutationResult, type MutationVariables } from "#graphql/registry";
|
|
3
|
+
type IsEmptyObject<T> = T extends Record<string, never> ? true : keyof T extends never ? true : false;
|
|
4
|
+
export declare function useGraphQLMutation<N extends MutationName>(operationName: N): {
|
|
5
|
+
mutate: (...args: IsEmptyObject<MutationVariables<N>> extends true ? [variables?: MutationVariables<N>] : [variables: MutationVariables<N>]) => Promise<MutationResult<N>>;
|
|
6
|
+
data: Ref<MutationResult<N> | null, MutationResult<N> | null>;
|
|
7
|
+
error: Ref<Error | null, Error | null>;
|
|
8
|
+
pending: Ref<boolean, boolean>;
|
|
9
|
+
};
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
import { useGraphQL } from "./useGraphQL.js";
|
|
3
|
+
import { mutations } from "#graphql/registry";
|
|
4
|
+
export function useGraphQLMutation(operationName) {
|
|
5
|
+
const document = mutations[operationName];
|
|
6
|
+
const { request } = useGraphQL();
|
|
7
|
+
const data = ref(null);
|
|
8
|
+
const error = ref(null);
|
|
9
|
+
const pending = ref(false);
|
|
10
|
+
async function mutate(...args) {
|
|
11
|
+
pending.value = true;
|
|
12
|
+
error.value = null;
|
|
13
|
+
try {
|
|
14
|
+
const result = await request(document, args[0]);
|
|
15
|
+
data.value = result;
|
|
16
|
+
return result;
|
|
17
|
+
} catch (e) {
|
|
18
|
+
error.value = e instanceof Error ? e : new Error(String(e));
|
|
19
|
+
throw e;
|
|
20
|
+
} finally {
|
|
21
|
+
pending.value = false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { mutate, data, error, pending };
|
|
25
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AsyncData, AsyncDataOptions } from "#app";
|
|
2
|
+
import { type QueryName, type QueryResult, type QueryVariables } from "#graphql/registry";
|
|
3
|
+
type IsEmptyObject<T> = T extends Record<string, never> ? true : keyof T extends never ? true : false;
|
|
4
|
+
export declare function useGraphQLQuery<N extends QueryName>(operationName: N, ...args: IsEmptyObject<QueryVariables<N>> extends true ? [variables?: QueryVariables<N>, opts?: AsyncDataOptions<QueryResult<N>>] : [variables: QueryVariables<N>, opts?: AsyncDataOptions<QueryResult<N>>]): AsyncData<QueryResult<N>, Error | null>;
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useAsyncData } from "#imports";
|
|
2
|
+
import { hash } from "ohash";
|
|
3
|
+
import { useGraphQL } from "./useGraphQL.js";
|
|
4
|
+
import { queries } from "#graphql/registry";
|
|
5
|
+
export function useGraphQLQuery(operationName, ...args) {
|
|
6
|
+
const document = queries[operationName];
|
|
7
|
+
const [variables, opts] = args;
|
|
8
|
+
const { request } = useGraphQL();
|
|
9
|
+
const key = `gql:q:${operationName}:${hash(variables ?? {})}`;
|
|
10
|
+
return useAsyncData(key, () => request(document, variables), opts);
|
|
11
|
+
}
|
package/dist/types.d.mts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lewebsimple/nuxt-graphql",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Opinionated Nuxt module for using GraphQL",
|
|
5
5
|
"repository": "lewebsimple/nuxt-graphql",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,17 +27,25 @@
|
|
|
27
27
|
"dev": "npm run dev:prepare && nuxi dev playground",
|
|
28
28
|
"dev:build": "nuxi build playground",
|
|
29
29
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
30
|
-
"release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish
|
|
30
|
+
"release": "npm run lint && npm run test && npm run prepack && changelogen --release --push && npm publish",
|
|
31
31
|
"lint": "eslint .",
|
|
32
32
|
"test": "vitest run",
|
|
33
33
|
"test:watch": "vitest watch",
|
|
34
34
|
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
+
"@graphql-codegen/cli": "^5.0.7",
|
|
38
|
+
"@graphql-codegen/typed-document-node": "^5.1.2",
|
|
39
|
+
"@graphql-codegen/typescript": "^4.1.6",
|
|
40
|
+
"@graphql-codegen/typescript-operations": "^4.6.1",
|
|
41
|
+
"@graphql-typed-document-node/core": "^3.2.0",
|
|
37
42
|
"@nuxt/kit": "^4.2.2",
|
|
38
43
|
"graphql": "^16.12.0",
|
|
39
44
|
"graphql-request": "^7.4.0",
|
|
40
|
-
"graphql-yoga": "^5.17.1"
|
|
45
|
+
"graphql-yoga": "^5.17.1",
|
|
46
|
+
"jiti": "^2.6.1",
|
|
47
|
+
"ohash": "^2.0.11",
|
|
48
|
+
"tinyglobby": "^0.2.15"
|
|
41
49
|
},
|
|
42
50
|
"devDependencies": {
|
|
43
51
|
"@nuxt/devtools": "^3.1.1",
|
|
@@ -56,5 +64,10 @@
|
|
|
56
64
|
},
|
|
57
65
|
"publishConfig": {
|
|
58
66
|
"access": "public"
|
|
67
|
+
},
|
|
68
|
+
"changlog": {
|
|
69
|
+
"types": {
|
|
70
|
+
"chore": false
|
|
71
|
+
}
|
|
59
72
|
}
|
|
60
73
|
}
|