@wpnuxt/core 2.2.1 → 2.3.0
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/app/graphqlMiddleware.clientOptions.ts +28 -0
- package/dist/module.d.mts +36 -0
- package/dist/module.d.ts +36 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +332 -92
- package/dist/runtime/composables/useWPContent.js +3 -2
- package/dist/runtime/internal/graphql-client.js +73 -0
- package/dist/runtime/types/stub.d.ts +5 -0
- package/dist/runtime/util/content.js +5 -1
- package/package.json +14 -11
- package/server/graphqlMiddleware.serverOptions.ts +45 -0
- package/dist/runtime/app/graphqlMiddleware.clientOptions.js +0 -12
- package/dist/runtime/server/graphqlMiddleware.serverOptions.d.ts +0 -0
- package/dist/runtime/server/graphqlMiddleware.serverOptions.js +0 -28
- /package/dist/runtime/{app/graphqlMiddleware.clientOptions.d.ts → internal/graphql-client.d.ts} +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineGraphqlClientOptions } from 'nuxt-graphql-middleware/client-options'
|
|
2
|
+
import { useRoute } from '#imports'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* WPNuxt default client options for nuxt-graphql-middleware.
|
|
6
|
+
*
|
|
7
|
+
* This enables passing client context to the server for:
|
|
8
|
+
* - Preview mode (passes preview flag and token from URL query params)
|
|
9
|
+
*
|
|
10
|
+
* The context is available in serverFetchOptions via context.client
|
|
11
|
+
* All values must be strings (nuxt-graphql-middleware requirement)
|
|
12
|
+
*
|
|
13
|
+
* Users can customize by creating their own app/graphqlMiddleware.clientOptions.ts
|
|
14
|
+
*/
|
|
15
|
+
export default defineGraphqlClientOptions<{
|
|
16
|
+
preview?: string
|
|
17
|
+
previewToken?: string
|
|
18
|
+
}>({
|
|
19
|
+
buildClientContext() {
|
|
20
|
+
const route = useRoute()
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
// Context values must be strings - use 'true'/'false' instead of boolean
|
|
24
|
+
preview: route.query.preview === 'true' ? 'true' : undefined,
|
|
25
|
+
previewToken: route.query.token as string | undefined
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})
|
package/dist/module.d.mts
CHANGED
|
@@ -127,6 +127,42 @@ interface WPNuxtConfig {
|
|
|
127
127
|
*/
|
|
128
128
|
revalidateSecret?: string;
|
|
129
129
|
};
|
|
130
|
+
/**
|
|
131
|
+
* Auto-generation of fragments + queries for Custom Post Types.
|
|
132
|
+
*
|
|
133
|
+
* WPNuxt parses the downloaded `schema.graphql` at build time, finds every
|
|
134
|
+
* object type implementing `ContentNode`, and emits a base fragment plus
|
|
135
|
+
* `Listing`, `ByUri`, and `BySlug` queries for each discovered CPT. Built-in
|
|
136
|
+
* types (Post, Page, MediaItem, Revision, Comment) are excluded because
|
|
137
|
+
* they already have default queries.
|
|
138
|
+
*
|
|
139
|
+
* Generated files land in `.queries/` and can be fully overridden by
|
|
140
|
+
* dropping a file with the same name in `extend/queries/fragments/`
|
|
141
|
+
* (for fragments) or `extend/queries/` (for queries).
|
|
142
|
+
*/
|
|
143
|
+
cpt?: {
|
|
144
|
+
/**
|
|
145
|
+
* Enable CPT auto-generation.
|
|
146
|
+
*
|
|
147
|
+
* @default true
|
|
148
|
+
*/
|
|
149
|
+
enabled?: boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Type names to skip in addition to the built-in exclusions.
|
|
152
|
+
*
|
|
153
|
+
* @example ['DraftPost', 'InternalNote']
|
|
154
|
+
*/
|
|
155
|
+
exclude?: string[];
|
|
156
|
+
/**
|
|
157
|
+
* If set, only these type names will be auto-generated.
|
|
158
|
+
*
|
|
159
|
+
* Useful when you want fine-grained control over which CPTs get
|
|
160
|
+
* auto-generated output. Built-in exclusions still apply.
|
|
161
|
+
*
|
|
162
|
+
* @example ['Event', 'Artist']
|
|
163
|
+
*/
|
|
164
|
+
include?: string[];
|
|
165
|
+
};
|
|
130
166
|
}
|
|
131
167
|
|
|
132
168
|
declare const _default: _nuxt_schema.NuxtModule<WPNuxtConfig, WPNuxtConfig, false>;
|
package/dist/module.d.ts
CHANGED
|
@@ -127,6 +127,42 @@ interface WPNuxtConfig {
|
|
|
127
127
|
*/
|
|
128
128
|
revalidateSecret?: string;
|
|
129
129
|
};
|
|
130
|
+
/**
|
|
131
|
+
* Auto-generation of fragments + queries for Custom Post Types.
|
|
132
|
+
*
|
|
133
|
+
* WPNuxt parses the downloaded `schema.graphql` at build time, finds every
|
|
134
|
+
* object type implementing `ContentNode`, and emits a base fragment plus
|
|
135
|
+
* `Listing`, `ByUri`, and `BySlug` queries for each discovered CPT. Built-in
|
|
136
|
+
* types (Post, Page, MediaItem, Revision, Comment) are excluded because
|
|
137
|
+
* they already have default queries.
|
|
138
|
+
*
|
|
139
|
+
* Generated files land in `.queries/` and can be fully overridden by
|
|
140
|
+
* dropping a file with the same name in `extend/queries/fragments/`
|
|
141
|
+
* (for fragments) or `extend/queries/` (for queries).
|
|
142
|
+
*/
|
|
143
|
+
cpt?: {
|
|
144
|
+
/**
|
|
145
|
+
* Enable CPT auto-generation.
|
|
146
|
+
*
|
|
147
|
+
* @default true
|
|
148
|
+
*/
|
|
149
|
+
enabled?: boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Type names to skip in addition to the built-in exclusions.
|
|
152
|
+
*
|
|
153
|
+
* @example ['DraftPost', 'InternalNote']
|
|
154
|
+
*/
|
|
155
|
+
exclude?: string[];
|
|
156
|
+
/**
|
|
157
|
+
* If set, only these type names will be auto-generated.
|
|
158
|
+
*
|
|
159
|
+
* Useful when you want fine-grained control over which CPTs get
|
|
160
|
+
* auto-generated output. Built-in exclusions still apply.
|
|
161
|
+
*
|
|
162
|
+
* @example ['Event', 'Artist']
|
|
163
|
+
*/
|
|
164
|
+
include?: string[];
|
|
165
|
+
};
|
|
130
166
|
}
|
|
131
167
|
|
|
132
168
|
declare const _default: _nuxt_schema.NuxtModule<WPNuxtConfig, WPNuxtConfig, false>;
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,14 +1,175 @@
|
|
|
1
1
|
import { defu } from 'defu';
|
|
2
|
-
import { promises, cpSync,
|
|
3
|
-
import { writeFile, rename, readFile
|
|
2
|
+
import { existsSync, readFileSync, promises, cpSync, readdirSync, statSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { mkdir, writeFile, rename, readFile } from 'node:fs/promises';
|
|
4
4
|
import { join, relative, dirname } from 'node:path';
|
|
5
5
|
import { useLogger, createResolver, resolveFiles, defineNuxtModule, addPlugin, addServerHandler, addImports, addComponentsDir, addTemplate, addTypeTemplate, hasNuxtModule, installModule } from '@nuxt/kit';
|
|
6
6
|
import { upperFirst } from 'scule';
|
|
7
7
|
import { parse, GraphQLError, visit, print } from 'graphql';
|
|
8
|
+
import ts from 'typescript';
|
|
8
9
|
import { execSync } from 'node:child_process';
|
|
9
10
|
import { consola } from 'consola';
|
|
10
11
|
|
|
11
|
-
const version = "2.
|
|
12
|
+
const version = "2.3.0";
|
|
13
|
+
|
|
14
|
+
const DEFAULT_CPT_EXCLUSIONS = [
|
|
15
|
+
"Post",
|
|
16
|
+
"Page",
|
|
17
|
+
"MediaItem",
|
|
18
|
+
"Revision",
|
|
19
|
+
"Comment",
|
|
20
|
+
"ActionMonitorAction"
|
|
21
|
+
];
|
|
22
|
+
function discoverCpts(schemaPath, options = {}) {
|
|
23
|
+
if (!existsSync(schemaPath)) return [];
|
|
24
|
+
let ast;
|
|
25
|
+
try {
|
|
26
|
+
ast = parse(readFileSync(schemaPath, "utf8"), { noLocation: true });
|
|
27
|
+
} catch {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
const typesByName = /* @__PURE__ */ new Map();
|
|
31
|
+
const enumsByName = /* @__PURE__ */ new Map();
|
|
32
|
+
for (const def of ast.definitions) {
|
|
33
|
+
if (def.kind === "ObjectTypeDefinition") typesByName.set(def.name.value, def);
|
|
34
|
+
else if (def.kind === "EnumTypeDefinition") enumsByName.set(def.name.value, def);
|
|
35
|
+
}
|
|
36
|
+
const rootQuery = typesByName.get("RootQuery");
|
|
37
|
+
if (!rootQuery?.fields) return [];
|
|
38
|
+
const fieldsByType = /* @__PURE__ */ new Map();
|
|
39
|
+
for (const field of rootQuery.fields) {
|
|
40
|
+
const returnType = unwrapNamedType(field.type);
|
|
41
|
+
if (!returnType) continue;
|
|
42
|
+
const hasIdType = field.arguments?.some((a) => a.name.value === "idType");
|
|
43
|
+
if (typesByName.has(returnType) && hasIdType) {
|
|
44
|
+
const idTypeArg = field.arguments?.find((a) => a.name.value === "idType");
|
|
45
|
+
const idTypeEnum = idTypeArg ? unwrapNamedType(idTypeArg.type) : void 0;
|
|
46
|
+
const entry = fieldsByType.get(returnType) ?? {};
|
|
47
|
+
entry.single = field.name.value;
|
|
48
|
+
entry.idTypeEnum = idTypeEnum;
|
|
49
|
+
fieldsByType.set(returnType, entry);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const connMatch = /^RootQueryTo(\w+)Connection$/.exec(returnType);
|
|
53
|
+
const hasCursorArgs = field.arguments?.some((a) => a.name.value === "first") && field.arguments?.some((a) => a.name.value === "after");
|
|
54
|
+
if (connMatch && hasCursorArgs) {
|
|
55
|
+
const typeName = connMatch[1];
|
|
56
|
+
const entry = fieldsByType.get(typeName) ?? {};
|
|
57
|
+
entry.connection = field.name.value;
|
|
58
|
+
fieldsByType.set(typeName, entry);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const excludeSet = /* @__PURE__ */ new Set([...DEFAULT_CPT_EXCLUSIONS, ...options.exclude ?? []]);
|
|
62
|
+
const includeSet = options.include?.length ? new Set(options.include) : void 0;
|
|
63
|
+
const result = [];
|
|
64
|
+
for (const [typeName, node] of typesByName) {
|
|
65
|
+
if (!typeImplements(node, "ContentNode")) continue;
|
|
66
|
+
if (excludeSet.has(typeName)) continue;
|
|
67
|
+
if (includeSet && !includeSet.has(typeName)) continue;
|
|
68
|
+
const queryFields = fieldsByType.get(typeName);
|
|
69
|
+
if (!queryFields?.single || !queryFields.connection) continue;
|
|
70
|
+
const supportedIdTypes = /* @__PURE__ */ new Set();
|
|
71
|
+
if (queryFields.idTypeEnum) {
|
|
72
|
+
const enumDef = enumsByName.get(queryFields.idTypeEnum);
|
|
73
|
+
enumDef?.values?.forEach((v) => supportedIdTypes.add(v.name.value));
|
|
74
|
+
}
|
|
75
|
+
result.push({
|
|
76
|
+
typeName,
|
|
77
|
+
singleField: queryFields.single,
|
|
78
|
+
connectionField: queryFields.connection,
|
|
79
|
+
idTypeEnum: queryFields.idTypeEnum,
|
|
80
|
+
supportedIdTypes,
|
|
81
|
+
interfaces: new Set((node.interfaces ?? []).map((i) => i.name.value)),
|
|
82
|
+
hasField: collectScalarFieldFlags(node, ["title", "slug", "uri", "date"])
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return result.sort((a, b) => a.typeName.localeCompare(b.typeName));
|
|
86
|
+
}
|
|
87
|
+
function unwrapNamedType(type) {
|
|
88
|
+
let current = type;
|
|
89
|
+
while (current) {
|
|
90
|
+
if (current.kind === "NamedType") return current.name.value;
|
|
91
|
+
current = current.type;
|
|
92
|
+
}
|
|
93
|
+
return void 0;
|
|
94
|
+
}
|
|
95
|
+
function typeImplements(node, interfaceName) {
|
|
96
|
+
return node.interfaces?.some((i) => i.name.value === interfaceName) ?? false;
|
|
97
|
+
}
|
|
98
|
+
function collectScalarFieldFlags(node, fieldNames) {
|
|
99
|
+
const flags = {};
|
|
100
|
+
const declared = new Set((node.fields ?? []).map((f) => f.name.value));
|
|
101
|
+
for (const name of fieldNames) flags[name] = declared.has(name);
|
|
102
|
+
return flags;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function writeCptArtifacts(cpt, outputPath) {
|
|
106
|
+
const fragmentsDir = join(outputPath, "fragments");
|
|
107
|
+
const fragmentPath = join(fragmentsDir, `${cpt.typeName}.fragment.gql`);
|
|
108
|
+
const queryPath = join(outputPath, `${upperFirst(cpt.connectionField)}.gql`);
|
|
109
|
+
const wroteFragment = !existsSync(fragmentPath);
|
|
110
|
+
const wroteQueries = !existsSync(queryPath);
|
|
111
|
+
if (wroteFragment) {
|
|
112
|
+
await mkdir(fragmentsDir, { recursive: true });
|
|
113
|
+
await writeFile(fragmentPath, buildCptFragment(cpt), "utf-8");
|
|
114
|
+
}
|
|
115
|
+
if (wroteQueries) {
|
|
116
|
+
await writeFile(queryPath, buildCptQueries(cpt), "utf-8");
|
|
117
|
+
}
|
|
118
|
+
return { cpt, wroteFragment, wroteQueries };
|
|
119
|
+
}
|
|
120
|
+
function buildCptFragment(cpt) {
|
|
121
|
+
const lines = [`fragment ${cpt.typeName} on ${cpt.typeName} {`];
|
|
122
|
+
lines.push(" ...ContentNode");
|
|
123
|
+
if (cpt.interfaces.has("NodeWithExcerpt")) lines.push(" ...NodeWithExcerpt");
|
|
124
|
+
if (cpt.interfaces.has("NodeWithContentEditor")) lines.push(" ...NodeWithContentEditor");
|
|
125
|
+
if (cpt.interfaces.has("NodeWithFeaturedImage")) lines.push(" ...NodeWithFeaturedImage");
|
|
126
|
+
if (cpt.hasField.title) lines.push(" title");
|
|
127
|
+
lines.push("}");
|
|
128
|
+
lines.push("");
|
|
129
|
+
return lines.join("\n");
|
|
130
|
+
}
|
|
131
|
+
function buildCptQueries(cpt) {
|
|
132
|
+
const { typeName, singleField, connectionField, supportedIdTypes } = cpt;
|
|
133
|
+
const listQueryName = upperFirst(connectionField);
|
|
134
|
+
const queries = [];
|
|
135
|
+
queries.push(
|
|
136
|
+
`query ${listQueryName}($first: Int = 20, $after: String) {`,
|
|
137
|
+
` ${connectionField}(first: $first, after: $after) {`,
|
|
138
|
+
` nodes {`,
|
|
139
|
+
` ...${typeName}`,
|
|
140
|
+
` }`,
|
|
141
|
+
` pageInfo {`,
|
|
142
|
+
` hasNextPage`,
|
|
143
|
+
` hasPreviousPage`,
|
|
144
|
+
` startCursor`,
|
|
145
|
+
` endCursor`,
|
|
146
|
+
` }`,
|
|
147
|
+
` }`,
|
|
148
|
+
`}`,
|
|
149
|
+
""
|
|
150
|
+
);
|
|
151
|
+
if (supportedIdTypes.has("URI")) {
|
|
152
|
+
queries.push(
|
|
153
|
+
`query ${typeName}ByUri($uri: ID!) {`,
|
|
154
|
+
` ${singleField}(id: $uri, idType: URI) {`,
|
|
155
|
+
` ...${typeName}`,
|
|
156
|
+
` }`,
|
|
157
|
+
`}`,
|
|
158
|
+
""
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
if (supportedIdTypes.has("SLUG")) {
|
|
162
|
+
queries.push(
|
|
163
|
+
`query ${typeName}BySlug($slug: ID!) {`,
|
|
164
|
+
` ${singleField}(id: $slug, idType: SLUG) {`,
|
|
165
|
+
` ...${typeName}`,
|
|
166
|
+
` }`,
|
|
167
|
+
`}`,
|
|
168
|
+
""
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return queries.join("\n");
|
|
172
|
+
}
|
|
12
173
|
|
|
13
174
|
function createModuleError(module, message) {
|
|
14
175
|
return new Error(formatErrorMessage(module, message));
|
|
@@ -51,7 +212,7 @@ const initLogger = (debug) => {
|
|
|
51
212
|
function getLogger() {
|
|
52
213
|
return loggerInstance;
|
|
53
214
|
}
|
|
54
|
-
async function mergeQueries(nuxt, wpNuxtConfig, resolver) {
|
|
215
|
+
async function mergeQueries(nuxt, wpNuxtConfig, resolver, schemaPath) {
|
|
55
216
|
const logger = getLogger();
|
|
56
217
|
const baseDir = nuxt.options.srcDir || nuxt.options.rootDir;
|
|
57
218
|
const { resolve } = createResolver(baseDir);
|
|
@@ -60,6 +221,20 @@ async function mergeQueries(nuxt, wpNuxtConfig, resolver) {
|
|
|
60
221
|
const defaultQueriesPath = resolver.resolve("./runtime/queries");
|
|
61
222
|
await promises.rm(queryOutputPath, { recursive: true, force: true });
|
|
62
223
|
cpSync(defaultQueriesPath, queryOutputPath, { recursive: true });
|
|
224
|
+
const cptSpreads = [];
|
|
225
|
+
if (schemaPath && wpNuxtConfig.cpt?.enabled !== false) {
|
|
226
|
+
const cpts = discoverCpts(schemaPath, {
|
|
227
|
+
exclude: wpNuxtConfig.cpt?.exclude,
|
|
228
|
+
include: wpNuxtConfig.cpt?.include
|
|
229
|
+
});
|
|
230
|
+
for (const cpt of cpts) {
|
|
231
|
+
await writeCptArtifacts(cpt, queryOutputPath);
|
|
232
|
+
cptSpreads.push({ name: cpt.typeName, type: cpt.typeName });
|
|
233
|
+
}
|
|
234
|
+
if (cpts.length) {
|
|
235
|
+
logger.debug(`Auto-generated fragments + queries for CPTs: ${cpts.map((c) => c.typeName).join(", ")}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
63
238
|
const conflicts = findConflicts(userQueryPath, queryOutputPath);
|
|
64
239
|
if (conflicts.length && wpNuxtConfig.queries.warnOnOverride) {
|
|
65
240
|
logger.warn("The following user query files will override default queries:");
|
|
@@ -71,26 +246,29 @@ async function mergeQueries(nuxt, wpNuxtConfig, resolver) {
|
|
|
71
246
|
logger.debug("Extending queries:", userQueryPath);
|
|
72
247
|
copyGraphqlFiles(userQueryPath, queryOutputPath);
|
|
73
248
|
}
|
|
74
|
-
await addCustomFragmentsToNodeQuery(queryOutputPath, userQueryPath, logger);
|
|
249
|
+
await addCustomFragmentsToNodeQuery(queryOutputPath, userQueryPath, logger, cptSpreads);
|
|
75
250
|
logger.debug("Merged queries folder:", queryOutputPath);
|
|
76
251
|
return queryOutputPath;
|
|
77
252
|
}
|
|
78
253
|
const FRAGMENT_DEF_PATTERN = /fragment\s+(\w+)\s+on\s+(\w+)/;
|
|
79
|
-
async function addCustomFragmentsToNodeQuery(queryOutputPath, userQueryPath, logger) {
|
|
254
|
+
async function addCustomFragmentsToNodeQuery(queryOutputPath, userQueryPath, logger, preDiscovered = []) {
|
|
255
|
+
const byType = /* @__PURE__ */ new Map();
|
|
256
|
+
for (const f of preDiscovered) byType.set(f.type, f);
|
|
80
257
|
const userFragmentsDir = join(userQueryPath, "fragments");
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
258
|
+
if (existsSync(userFragmentsDir)) {
|
|
259
|
+
for (const file of readdirSync(userFragmentsDir)) {
|
|
260
|
+
if (!file.endsWith(".gql") && !file.endsWith(".graphql")) continue;
|
|
261
|
+
const content = await promises.readFile(join(userFragmentsDir, file), "utf-8");
|
|
262
|
+
const match = content.match(FRAGMENT_DEF_PATTERN);
|
|
263
|
+
if (!match) continue;
|
|
264
|
+
const name = match[1];
|
|
265
|
+
const type = match[2];
|
|
266
|
+
if (name && type && name === type) {
|
|
267
|
+
byType.set(type, { name, type });
|
|
268
|
+
}
|
|
92
269
|
}
|
|
93
270
|
}
|
|
271
|
+
const customFragments = [...byType.values()];
|
|
94
272
|
if (customFragments.length === 0) return;
|
|
95
273
|
const nodeGqlPath = join(queryOutputPath, "Node.gql");
|
|
96
274
|
if (!existsSync(nodeGqlPath)) return;
|
|
@@ -204,6 +382,46 @@ function processSelections(selections, level, query, canExtract = true) {
|
|
|
204
382
|
}
|
|
205
383
|
const parseDoc = _parseDoc;
|
|
206
384
|
|
|
385
|
+
const { factory, SyntaxKind, NewLineKind, EmitHint, ScriptTarget } = ts;
|
|
386
|
+
function typeRef(name, typeArguments) {
|
|
387
|
+
return factory.createTypeReferenceNode(name, typeArguments);
|
|
388
|
+
}
|
|
389
|
+
function nonNullable(node) {
|
|
390
|
+
return typeRef("NonNullable", [node]);
|
|
391
|
+
}
|
|
392
|
+
function withImagePath(node) {
|
|
393
|
+
return typeRef("WithImagePath", [node]);
|
|
394
|
+
}
|
|
395
|
+
function indexedAccess(target, key) {
|
|
396
|
+
return factory.createIndexedAccessTypeNode(
|
|
397
|
+
target,
|
|
398
|
+
// `isSingleQuote` preserves the single-quote style the generator used
|
|
399
|
+
// before this module was AST-based — keeps generated .d.ts text-stable.
|
|
400
|
+
factory.createLiteralTypeNode(factory.createStringLiteral(key, true))
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
function numberIndexedAccess(target) {
|
|
404
|
+
return factory.createIndexedAccessTypeNode(
|
|
405
|
+
target,
|
|
406
|
+
factory.createKeywordTypeNode(SyntaxKind.NumberKeyword)
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
function arrayOf(node) {
|
|
410
|
+
return factory.createArrayTypeNode(node);
|
|
411
|
+
}
|
|
412
|
+
function unionOrSingle(nodes) {
|
|
413
|
+
if (nodes.length === 0) {
|
|
414
|
+
throw new Error("unionOrSingle: empty type list");
|
|
415
|
+
}
|
|
416
|
+
if (nodes.length === 1) return nodes[0];
|
|
417
|
+
return factory.createUnionTypeNode(nodes);
|
|
418
|
+
}
|
|
419
|
+
const printer = ts.createPrinter({ newLine: NewLineKind.LineFeed, removeComments: false });
|
|
420
|
+
const syntheticFile = ts.createSourceFile("__synthetic.ts", "", ScriptTarget.Latest);
|
|
421
|
+
function printType(node) {
|
|
422
|
+
return printer.printNode(EmitHint.Unspecified, node, syntheticFile);
|
|
423
|
+
}
|
|
424
|
+
|
|
207
425
|
const SCHEMA_PATTERN = /schema\.(?:gql|graphql)$/i;
|
|
208
426
|
const COMPLEXITY_THRESHOLDS = {
|
|
209
427
|
/** Maximum recommended extraction depth */
|
|
@@ -257,37 +475,40 @@ async function prepareContext(ctx) {
|
|
|
257
475
|
const fnName = (fn) => ctx.composablesPrefix + upperFirst(fn);
|
|
258
476
|
const mutationFnName = (fn) => `useMutation${upperFirst(fn)}`;
|
|
259
477
|
const formatNodes = (nodes) => nodes?.map((n) => `'${n}'`).join(",") ?? "";
|
|
260
|
-
const
|
|
478
|
+
const buildFragmentTypeNode = (q) => {
|
|
261
479
|
if (q.hasPageInfo) {
|
|
262
480
|
if (q.hasInlineFields || !q.fragments?.length) {
|
|
263
481
|
if (q.nodes?.length) {
|
|
264
|
-
let
|
|
265
|
-
for (const
|
|
266
|
-
|
|
482
|
+
let node = typeRef(`${q.name}RootQuery`);
|
|
483
|
+
for (const segment of q.nodes) {
|
|
484
|
+
node = indexedAccess(nonNullable(node), segment);
|
|
267
485
|
}
|
|
268
|
-
|
|
269
|
-
return typePath;
|
|
486
|
+
return numberIndexedAccess(indexedAccess(nonNullable(node), "nodes"));
|
|
270
487
|
}
|
|
271
|
-
return `${q.name}RootQuery
|
|
488
|
+
return typeRef(`${q.name}RootQuery`);
|
|
272
489
|
}
|
|
273
|
-
return q.fragments.map((f) =>
|
|
490
|
+
return unionOrSingle(q.fragments.map((f) => withImagePath(typeRef(`${f}Fragment`))));
|
|
274
491
|
}
|
|
275
492
|
if (q.hasInlineFields || !q.fragments?.length) {
|
|
276
493
|
if (q.nodes?.length) {
|
|
277
|
-
let
|
|
278
|
-
for (const
|
|
279
|
-
|
|
494
|
+
let node = typeRef(`${q.name}RootQuery`);
|
|
495
|
+
for (const segment of q.nodes) {
|
|
496
|
+
node = indexedAccess(nonNullable(node), segment);
|
|
280
497
|
}
|
|
281
498
|
if (q.nodes.includes("nodes")) {
|
|
282
|
-
|
|
499
|
+
node = numberIndexedAccess(node);
|
|
283
500
|
}
|
|
284
|
-
return
|
|
501
|
+
return node;
|
|
285
502
|
}
|
|
286
|
-
return `${q.name}RootQuery
|
|
503
|
+
return typeRef(`${q.name}RootQuery`);
|
|
504
|
+
}
|
|
505
|
+
const fragmentTypes = q.fragments.map((f) => withImagePath(typeRef(`${f}Fragment`)));
|
|
506
|
+
if (q.nodes?.includes("nodes")) {
|
|
507
|
+
return unionOrSingle(fragmentTypes.map(arrayOf));
|
|
287
508
|
}
|
|
288
|
-
|
|
289
|
-
return q.fragments.map((f) => `WithImagePath<${f}Fragment>${fragmentSuffix}`).join(" | ");
|
|
509
|
+
return unionOrSingle(fragmentTypes);
|
|
290
510
|
};
|
|
511
|
+
const getFragmentType = (q) => printType(buildFragmentTypeNode(q));
|
|
291
512
|
const queryFnExp = (q, typed = false) => {
|
|
292
513
|
const functionName = fnName(q.name);
|
|
293
514
|
if (q.hasPageInfo) {
|
|
@@ -304,7 +525,7 @@ async function prepareContext(ctx) {
|
|
|
304
525
|
const mutationFnExp = (m, typed = false) => {
|
|
305
526
|
const functionName = mutationFnName(m.name);
|
|
306
527
|
if (!typed) {
|
|
307
|
-
return `export const ${functionName} = (variables, options) =>
|
|
528
|
+
return `export const ${functionName} = (variables, options) => wpMutation('${m.name}', variables, options)`;
|
|
308
529
|
}
|
|
309
530
|
return ` export const ${functionName}: (variables: ${m.name}MutationVariables, options?: WPMutationOptions) => Promise<WPMutationResult<${m.name}Mutation>>`;
|
|
310
531
|
};
|
|
@@ -319,11 +540,13 @@ async function prepareContext(ctx) {
|
|
|
319
540
|
if (hasConnectionQueries) {
|
|
320
541
|
imports.push("useWPConnection");
|
|
321
542
|
}
|
|
322
|
-
if (mutations.length > 0) {
|
|
323
|
-
imports.push("useGraphqlMutation");
|
|
324
|
-
}
|
|
325
543
|
if (imports.length > 0) {
|
|
326
544
|
lines.push(`import { ${imports.join(", ")} } from '#imports'`);
|
|
545
|
+
}
|
|
546
|
+
if (mutations.length > 0) {
|
|
547
|
+
lines.push(`import { wpMutation } from '#wpnuxt-internal'`);
|
|
548
|
+
}
|
|
549
|
+
if (lines.length > 0) {
|
|
327
550
|
lines.push("");
|
|
328
551
|
}
|
|
329
552
|
queries.forEach((f) => {
|
|
@@ -347,6 +570,7 @@ async function prepareContext(ctx) {
|
|
|
347
570
|
typeSet.add(`${m.name}MutationVariables`);
|
|
348
571
|
typeSet.add(`${m.name}Mutation`);
|
|
349
572
|
});
|
|
573
|
+
ctx.referencedTypes = [...typeSet];
|
|
350
574
|
ctx.generateDeclarations = () => {
|
|
351
575
|
const declarations = [
|
|
352
576
|
`import type { ${[...typeSet].join(", ")} } from '#build/graphql-operations'`,
|
|
@@ -518,7 +742,7 @@ Make sure WPGraphQL plugin is installed and activated on your WordPress site.`
|
|
|
518
742
|
);
|
|
519
743
|
}
|
|
520
744
|
await checkWPGraphQLVersion(fullUrl, headers);
|
|
521
|
-
if (options.schemaPath
|
|
745
|
+
if (options.schemaPath) {
|
|
522
746
|
try {
|
|
523
747
|
const authFlag = options.authToken ? ` -h "Authorization=Bearer ${options.authToken}"` : "";
|
|
524
748
|
execSync(`npx get-graphql-schema "${fullUrl}"${authFlag} > "${options.schemaPath}"`, {
|
|
@@ -649,6 +873,40 @@ function patchWPGraphQLSchema(schemaPath) {
|
|
|
649
873
|
writeFileSync(schemaPath, patchSchemaText(schema));
|
|
650
874
|
}
|
|
651
875
|
|
|
876
|
+
function validateGeneratedPaths(referencedTypes, operationsDtsPath) {
|
|
877
|
+
if (!existsSync(operationsDtsPath)) {
|
|
878
|
+
return { skipped: true, dangling: [] };
|
|
879
|
+
}
|
|
880
|
+
const source = readFileSync(operationsDtsPath, "utf8");
|
|
881
|
+
const sourceFile = ts.createSourceFile(
|
|
882
|
+
operationsDtsPath,
|
|
883
|
+
source,
|
|
884
|
+
ts.ScriptTarget.Latest,
|
|
885
|
+
false
|
|
886
|
+
);
|
|
887
|
+
const declared = /* @__PURE__ */ new Set();
|
|
888
|
+
const visit = (node) => {
|
|
889
|
+
if (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isClassDeclaration(node)) {
|
|
890
|
+
if (node.name) declared.add(node.name.text);
|
|
891
|
+
} else if (ts.isVariableStatement(node)) {
|
|
892
|
+
for (const d of node.declarationList.declarations) {
|
|
893
|
+
if (ts.isIdentifier(d.name)) declared.add(d.name.text);
|
|
894
|
+
}
|
|
895
|
+
} else if (ts.isModuleDeclaration(node) && node.body && ts.isModuleBlock(node.body)) {
|
|
896
|
+
for (const child of node.body.statements) visit(child);
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
for (const stmt of sourceFile.statements) visit(stmt);
|
|
900
|
+
const dangling = [];
|
|
901
|
+
const seen = /* @__PURE__ */ new Set();
|
|
902
|
+
for (const ref of referencedTypes) {
|
|
903
|
+
if (seen.has(ref)) continue;
|
|
904
|
+
seen.add(ref);
|
|
905
|
+
if (!declared.has(ref)) dangling.push(ref);
|
|
906
|
+
}
|
|
907
|
+
return { skipped: false, dangling };
|
|
908
|
+
}
|
|
909
|
+
|
|
652
910
|
async function runInstall(nuxt) {
|
|
653
911
|
const logger = useLogger("wpnuxt", {
|
|
654
912
|
level: process.env.WPNUXT_DEBUG === "true" ? 4 : 3
|
|
@@ -872,6 +1130,11 @@ const module$1 = defineNuxtModule({
|
|
|
872
1130
|
maxAge: 60 * 5,
|
|
873
1131
|
// 5 minutes
|
|
874
1132
|
swr: true
|
|
1133
|
+
},
|
|
1134
|
+
cpt: {
|
|
1135
|
+
enabled: true,
|
|
1136
|
+
exclude: [],
|
|
1137
|
+
include: []
|
|
875
1138
|
}
|
|
876
1139
|
},
|
|
877
1140
|
async setup(options, nuxt) {
|
|
@@ -890,37 +1153,36 @@ const module$1 = defineNuxtModule({
|
|
|
890
1153
|
addPlugin(resolver.resolve("./runtime/plugins/graphqlErrors"));
|
|
891
1154
|
addPlugin(resolver.resolve("./runtime/plugins/sanitizeHtml"));
|
|
892
1155
|
configureTrailingSlash(nuxt, logger);
|
|
893
|
-
const
|
|
894
|
-
|
|
895
|
-
|
|
1156
|
+
const packageRoot = resolver.resolve("..");
|
|
1157
|
+
nuxt.options._layers.push({
|
|
1158
|
+
cwd: packageRoot,
|
|
1159
|
+
configFile: "",
|
|
1160
|
+
config: {
|
|
1161
|
+
rootDir: packageRoot,
|
|
1162
|
+
srcDir: packageRoot,
|
|
1163
|
+
serverDir: join(packageRoot, "server"),
|
|
1164
|
+
appDir: join(packageRoot, "app")
|
|
1165
|
+
}
|
|
1166
|
+
});
|
|
1167
|
+
logger.debug("Registered WPNuxt layer for graphqlMiddleware options auto-discovery");
|
|
896
1168
|
const schemaPath = join(nuxt.options.rootDir, "schema.graphql");
|
|
897
1169
|
const schemaExists = existsSync(schemaPath);
|
|
898
1170
|
if (wpNuxtConfig.downloadSchema) {
|
|
899
|
-
|
|
900
|
-
|
|
1171
|
+
logger.debug(`Downloading schema from: ${wpNuxtConfig.wordpressUrl}${wpNuxtConfig.graphqlEndpoint}`);
|
|
1172
|
+
try {
|
|
901
1173
|
await validateWordPressEndpoint(
|
|
902
1174
|
wpNuxtConfig.wordpressUrl,
|
|
903
1175
|
wpNuxtConfig.graphqlEndpoint,
|
|
904
1176
|
{ schemaPath, authToken: wpNuxtConfig.schemaAuthToken }
|
|
905
1177
|
);
|
|
906
1178
|
logger.debug("Schema downloaded successfully");
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
wpNuxtConfig.wordpressUrl,
|
|
912
|
-
wpNuxtConfig.graphqlEndpoint,
|
|
913
|
-
{ authToken: wpNuxtConfig.schemaAuthToken }
|
|
914
|
-
);
|
|
915
|
-
logger.debug("WordPress endpoint validation passed");
|
|
916
|
-
} catch (error) {
|
|
917
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
918
|
-
logger.warn(`WordPress endpoint validation failed: ${message.split("\n")[0]}`);
|
|
919
|
-
logger.warn("App will continue with existing schema.graphql file");
|
|
920
|
-
}
|
|
921
|
-
});
|
|
1179
|
+
} catch (error) {
|
|
1180
|
+
if (!schemaExists) throw error;
|
|
1181
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1182
|
+
logger.warn(`Schema refresh failed, using cached schema.graphql: ${message.split("\n")[0]}`);
|
|
922
1183
|
}
|
|
923
1184
|
}
|
|
1185
|
+
const mergedQueriesFolder = await mergeQueries(nuxt, wpNuxtConfig, resolver, schemaPath);
|
|
924
1186
|
await registerModules(nuxt, resolver, wpNuxtConfig, mergedQueriesFolder);
|
|
925
1187
|
nuxt.hook("devtools:customTabs", (tabs) => {
|
|
926
1188
|
const middlewareTab = tabs.find((tab) => tab.name === "nuxt-graphql-middleware");
|
|
@@ -1021,12 +1283,14 @@ const module$1 = defineNuxtModule({
|
|
|
1021
1283
|
nuxt.options.alias["#wpnuxt"] = resolver.resolve(nuxt.options.buildDir, "wpnuxt");
|
|
1022
1284
|
nuxt.options.alias["#wpnuxt/*"] = resolver.resolve(nuxt.options.buildDir, "wpnuxt", "*");
|
|
1023
1285
|
nuxt.options.alias["#wpnuxt/types"] = resolver.resolve("./types");
|
|
1286
|
+
nuxt.options.alias["#wpnuxt-internal"] = resolver.resolve("./runtime/internal/graphql-client");
|
|
1024
1287
|
nuxt.options.alias["@wpnuxt/core/server-options"] = resolver.resolve("./server-options");
|
|
1025
1288
|
nuxt.options.alias["@wpnuxt/core/client-options"] = resolver.resolve("./client-options");
|
|
1026
1289
|
const nitroOpts = nuxt.options;
|
|
1027
1290
|
nitroOpts.nitro = nitroOpts.nitro || {};
|
|
1028
1291
|
nitroOpts.nitro.alias = nitroOpts.nitro.alias || {};
|
|
1029
1292
|
nitroOpts.nitro.alias["#wpnuxt/types"] = resolver.resolve("./types");
|
|
1293
|
+
nitroOpts.nitro.alias["#wpnuxt-internal"] = resolver.resolve("./runtime/internal/graphql-client");
|
|
1030
1294
|
nitroOpts.nitro.externals = nitroOpts.nitro.externals || {};
|
|
1031
1295
|
nitroOpts.nitro.externals.inline = nitroOpts.nitro.externals.inline || [];
|
|
1032
1296
|
addTemplate({
|
|
@@ -1043,6 +1307,16 @@ const module$1 = defineNuxtModule({
|
|
|
1043
1307
|
autoimports.push(...ctx.fnImports || []);
|
|
1044
1308
|
});
|
|
1045
1309
|
logger.trace("Finished generating composables");
|
|
1310
|
+
nuxt.hook("build:before", () => {
|
|
1311
|
+
if (!ctx.referencedTypes?.length) return;
|
|
1312
|
+
const operationsDtsPath = join(nuxt.options.buildDir, "graphql-operations.d.ts");
|
|
1313
|
+
const result = validateGeneratedPaths(ctx.referencedTypes, operationsDtsPath);
|
|
1314
|
+
if (result.skipped || result.dangling.length === 0) return;
|
|
1315
|
+
logger.warn(
|
|
1316
|
+
`WPNuxt generated composables reference ${result.dangling.length} type(s) not declared in graphql-operations.d.ts. This usually means your WordPress GraphQL schema has drifted from your queries; delete schema.graphql to force a fresh download, then re-run pnpm dev:prepare.`
|
|
1317
|
+
);
|
|
1318
|
+
for (const t of result.dangling) logger.warn(` - ${t}`);
|
|
1319
|
+
});
|
|
1046
1320
|
logger.info(`WPNuxt module loaded in ${(/* @__PURE__ */ new Date()).getTime() - startTime}ms`);
|
|
1047
1321
|
},
|
|
1048
1322
|
async onInstall(nuxt) {
|
|
@@ -1091,40 +1365,6 @@ async function loadConfig(options, nuxt) {
|
|
|
1091
1365
|
}
|
|
1092
1366
|
return config;
|
|
1093
1367
|
}
|
|
1094
|
-
async function setupServerOptions(nuxt, resolver, logger) {
|
|
1095
|
-
const serverDir = nuxt.options.serverDir;
|
|
1096
|
-
const targetPath = join(serverDir, "graphqlMiddleware.serverOptions.ts");
|
|
1097
|
-
if (existsSync(targetPath)) {
|
|
1098
|
-
logger.debug("Using existing graphqlMiddleware.serverOptions.ts from project");
|
|
1099
|
-
return;
|
|
1100
|
-
}
|
|
1101
|
-
if (!existsSync(serverDir)) {
|
|
1102
|
-
await mkdir(serverDir, { recursive: true });
|
|
1103
|
-
}
|
|
1104
|
-
const template = readFileSync(
|
|
1105
|
-
resolver.resolve("./runtime/server/graphqlMiddleware.serverOptions.ts"),
|
|
1106
|
-
"utf-8"
|
|
1107
|
-
);
|
|
1108
|
-
await writeFile(targetPath, template);
|
|
1109
|
-
logger.debug("Created graphqlMiddleware.serverOptions.ts with WPNuxt defaults (cookie/auth forwarding)");
|
|
1110
|
-
}
|
|
1111
|
-
async function setupClientOptions(nuxt, resolver, logger) {
|
|
1112
|
-
const appDir = nuxt.options.dir.app;
|
|
1113
|
-
const targetPath = join(appDir, "graphqlMiddleware.clientOptions.ts");
|
|
1114
|
-
if (existsSync(targetPath)) {
|
|
1115
|
-
logger.debug("Using existing graphqlMiddleware.clientOptions.ts from project");
|
|
1116
|
-
return;
|
|
1117
|
-
}
|
|
1118
|
-
if (!existsSync(appDir)) {
|
|
1119
|
-
await mkdir(appDir, { recursive: true });
|
|
1120
|
-
}
|
|
1121
|
-
const template = readFileSync(
|
|
1122
|
-
resolver.resolve("./runtime/app/graphqlMiddleware.clientOptions.ts"),
|
|
1123
|
-
"utf-8"
|
|
1124
|
-
);
|
|
1125
|
-
await writeFile(targetPath, template);
|
|
1126
|
-
logger.debug("Created graphqlMiddleware.clientOptions.ts with WPNuxt defaults (preview mode support)");
|
|
1127
|
-
}
|
|
1128
1368
|
function configureTrailingSlash(nuxt, logger) {
|
|
1129
1369
|
const handlerPath = join(nuxt.options.buildDir, "wpnuxt", "trailing-slash-handler.ts").replace(/\\/g, "/");
|
|
1130
1370
|
const handlerCode = `import { defineEventHandler, sendRedirect, getRequestURL } from 'h3'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { transformData, normalizeUriParam } from "../util/content.js";
|
|
2
|
-
import { computed, ref, toValue, watch as vueWatch,
|
|
2
|
+
import { computed, ref, toValue, watch as vueWatch, useRuntimeConfig } from "#imports";
|
|
3
|
+
import { wpQuery } from "../internal/graphql-client.js";
|
|
3
4
|
const defaultGetCachedData = (key, app, ctx) => {
|
|
4
5
|
if (app.isHydrating) {
|
|
5
6
|
return app.payload.data[key];
|
|
@@ -58,7 +59,7 @@ export const useWPContent = (queryName, nodes, fixImagePaths, params, options) =
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
};
|
|
61
|
-
const asyncResult =
|
|
62
|
+
const asyncResult = wpQuery(
|
|
62
63
|
String(queryName),
|
|
63
64
|
resolvedParams,
|
|
64
65
|
asyncDataOptions
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { useAsyncGraphqlQuery, useGraphqlMutation, useNuxtApp, watch, toValue } from "#imports";
|
|
2
|
+
export const wpQuery = ((...args) => {
|
|
3
|
+
const [name, variables] = args;
|
|
4
|
+
const nuxtApp = useNuxtApp();
|
|
5
|
+
const result = useAsyncGraphqlQuery(...args);
|
|
6
|
+
let wasPending = false;
|
|
7
|
+
let cycleStart = 0;
|
|
8
|
+
const pendingSource = result.pending;
|
|
9
|
+
const errorSource = result.error;
|
|
10
|
+
watch(
|
|
11
|
+
pendingSource,
|
|
12
|
+
(isPending) => {
|
|
13
|
+
if (isPending) {
|
|
14
|
+
wasPending = true;
|
|
15
|
+
cycleStart = performance.now();
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (!wasPending) return;
|
|
19
|
+
wasPending = false;
|
|
20
|
+
const errorVal = errorSource?.value instanceof Error ? errorSource.value : void 0;
|
|
21
|
+
emitHook(nuxtApp, {
|
|
22
|
+
queryName: String(name),
|
|
23
|
+
queryType: "query",
|
|
24
|
+
variables: normalizeVariables(variables),
|
|
25
|
+
durationMs: performance.now() - cycleStart,
|
|
26
|
+
status: errorVal ? "error" : "success",
|
|
27
|
+
...errorVal ? { error: errorVal } : {}
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
{ immediate: true }
|
|
31
|
+
);
|
|
32
|
+
return result;
|
|
33
|
+
});
|
|
34
|
+
export const wpMutation = ((...args) => {
|
|
35
|
+
const [name, variables] = args;
|
|
36
|
+
const nuxtApp = useNuxtApp();
|
|
37
|
+
const startTime = performance.now();
|
|
38
|
+
const promise = useGraphqlMutation(...args);
|
|
39
|
+
Promise.resolve(promise).then(
|
|
40
|
+
() => emitHook(nuxtApp, {
|
|
41
|
+
queryName: String(name),
|
|
42
|
+
queryType: "mutation",
|
|
43
|
+
variables: normalizeVariables(variables),
|
|
44
|
+
durationMs: performance.now() - startTime,
|
|
45
|
+
status: "success"
|
|
46
|
+
}),
|
|
47
|
+
(err) => emitHook(nuxtApp, {
|
|
48
|
+
queryName: String(name),
|
|
49
|
+
queryType: "mutation",
|
|
50
|
+
variables: normalizeVariables(variables),
|
|
51
|
+
durationMs: performance.now() - startTime,
|
|
52
|
+
status: "error",
|
|
53
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
54
|
+
})
|
|
55
|
+
);
|
|
56
|
+
return promise;
|
|
57
|
+
});
|
|
58
|
+
function normalizeVariables(variables) {
|
|
59
|
+
const value = toValue(variables);
|
|
60
|
+
if (value && typeof value === "object") return value;
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
function emitHook(nuxtApp, payload) {
|
|
64
|
+
if (typeof nuxtApp?.callHook !== "function") return;
|
|
65
|
+
const maybePromise = nuxtApp.callHook("wpnuxt:query", payload);
|
|
66
|
+
if (maybePromise && typeof maybePromise.then === "function") {
|
|
67
|
+
Promise.resolve(maybePromise).catch((err) => {
|
|
68
|
+
if (import.meta.dev) {
|
|
69
|
+
console.warn("[wpnuxt] wpnuxt:query hook handler threw", err);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -42,6 +42,11 @@ export function useAsyncGraphqlQuery<T = unknown>(
|
|
|
42
42
|
status: Ref<string>
|
|
43
43
|
}
|
|
44
44
|
export function useGraphqlState(): Record<string, unknown>
|
|
45
|
+
export function useGraphqlMutation<T = unknown>(
|
|
46
|
+
name: string,
|
|
47
|
+
variables?: Record<string, unknown>,
|
|
48
|
+
options?: Record<string, unknown>
|
|
49
|
+
): Promise<{ data: T | null, errors?: Array<{ message: string }> | null }>
|
|
45
50
|
|
|
46
51
|
// Stub for #nuxt-graphql-middleware/operation-types
|
|
47
52
|
export type Query = Record<string, unknown>
|
|
@@ -23,7 +23,11 @@ export const transformData = (data, nodes, fixImagePaths) => {
|
|
|
23
23
|
if (fixImagePaths && transformedData) {
|
|
24
24
|
if (Array.isArray(transformedData)) {
|
|
25
25
|
transformedData.forEach(addRelativePath);
|
|
26
|
-
} else {
|
|
26
|
+
} else if (typeof transformedData === "object") {
|
|
27
|
+
const maybeNodes = transformedData.nodes;
|
|
28
|
+
if (Array.isArray(maybeNodes)) {
|
|
29
|
+
maybeNodes.forEach(addRelativePath);
|
|
30
|
+
}
|
|
27
31
|
addRelativePath(transformedData);
|
|
28
32
|
}
|
|
29
33
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wpnuxt/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Nuxt module for WordPress integration via GraphQL (WPGraphQL)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nuxt",
|
|
@@ -40,7 +40,9 @@
|
|
|
40
40
|
"main": "./dist/module.mjs",
|
|
41
41
|
"types": "./dist/types.d.mts",
|
|
42
42
|
"files": [
|
|
43
|
-
"dist"
|
|
43
|
+
"dist",
|
|
44
|
+
"server",
|
|
45
|
+
"app"
|
|
44
46
|
],
|
|
45
47
|
"publishConfig": {
|
|
46
48
|
"access": "public"
|
|
@@ -48,21 +50,22 @@
|
|
|
48
50
|
"dependencies": {
|
|
49
51
|
"@nuxt/kit": "4.4.2",
|
|
50
52
|
"consola": "^3.4.2",
|
|
51
|
-
"defu": "^6.1.
|
|
52
|
-
"dompurify": "^3.
|
|
53
|
-
"graphql": "^16.13.
|
|
54
|
-
"nuxt-graphql-middleware": "5.
|
|
55
|
-
"scule": "^1.3.0"
|
|
53
|
+
"defu": "^6.1.7",
|
|
54
|
+
"dompurify": "^3.4.0",
|
|
55
|
+
"graphql": "^16.13.2",
|
|
56
|
+
"nuxt-graphql-middleware": "5.4.0",
|
|
57
|
+
"scule": "^1.3.0",
|
|
58
|
+
"typescript": "^5.9.3"
|
|
56
59
|
},
|
|
57
60
|
"devDependencies": {
|
|
58
61
|
"@nuxt/devtools": "^3.2.3",
|
|
59
62
|
"@nuxt/module-builder": "^1.0.2",
|
|
60
63
|
"@nuxt/schema": "4.4.2",
|
|
61
|
-
"@nuxt/test-utils": "^4.0.
|
|
62
|
-
"@types/node": "^25.
|
|
64
|
+
"@nuxt/test-utils": "^4.0.2",
|
|
65
|
+
"@types/node": "^25.6.0",
|
|
63
66
|
"nuxt": "^4.4.2",
|
|
64
|
-
"vitest": "^4.1.
|
|
65
|
-
"vue-tsc": "^3.2.
|
|
67
|
+
"vitest": "^4.1.4",
|
|
68
|
+
"vue-tsc": "^3.2.6"
|
|
66
69
|
},
|
|
67
70
|
"peerDependencies": {
|
|
68
71
|
"nuxt": ">=4.0.0"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { defineGraphqlServerOptions } from 'nuxt-graphql-middleware/server-options'
|
|
2
|
+
import { getHeader, getCookie } from 'h3'
|
|
3
|
+
import { useRuntimeConfig } from '#imports'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* WPNuxt default server options for nuxt-graphql-middleware.
|
|
7
|
+
*
|
|
8
|
+
* This enables:
|
|
9
|
+
* - Cookie forwarding for WordPress preview mode
|
|
10
|
+
* - Authorization header forwarding for authenticated requests
|
|
11
|
+
* - Auth token from cookie for @wpnuxt/auth
|
|
12
|
+
* - Consistent error logging
|
|
13
|
+
*
|
|
14
|
+
* Users can customize by creating their own server/graphqlMiddleware.serverOptions.ts
|
|
15
|
+
*/
|
|
16
|
+
export default defineGraphqlServerOptions({
|
|
17
|
+
async serverFetchOptions(event, _operation, _operationName, _context) {
|
|
18
|
+
// Get auth token from Authorization header or from cookie
|
|
19
|
+
let authorization = getHeader(event, 'authorization') || ''
|
|
20
|
+
|
|
21
|
+
// If no Authorization header, check for auth token in cookie (@wpnuxt/auth)
|
|
22
|
+
if (!authorization) {
|
|
23
|
+
const config = (useRuntimeConfig().public as Record<string, unknown>).wpNuxtAuth as { cookieName?: string } | undefined
|
|
24
|
+
const cookieName = config?.cookieName || 'wpnuxt-auth-token'
|
|
25
|
+
const authToken = getCookie(event, cookieName)
|
|
26
|
+
if (authToken) {
|
|
27
|
+
authorization = `Bearer ${authToken}`
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
headers: {
|
|
33
|
+
// Forward WordPress auth cookies for previews
|
|
34
|
+
Cookie: getHeader(event, 'cookie') || '',
|
|
35
|
+
// Forward authorization header or token from cookie
|
|
36
|
+
Authorization: authorization
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
async onServerError(event, error, _operation, operationName) {
|
|
42
|
+
const url = event.node.req.url || 'unknown'
|
|
43
|
+
console.error(`[WPNuxt] GraphQL error in ${operationName} (${url}):`, error.message)
|
|
44
|
+
}
|
|
45
|
+
})
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { defineGraphqlClientOptions } from "@wpnuxt/core/client-options";
|
|
2
|
-
import { useRoute } from "#imports";
|
|
3
|
-
export default defineGraphqlClientOptions({
|
|
4
|
-
buildClientContext() {
|
|
5
|
-
const route = useRoute();
|
|
6
|
-
return {
|
|
7
|
-
// Context values must be strings - use 'true'/'false' instead of boolean
|
|
8
|
-
preview: route.query.preview === "true" ? "true" : void 0,
|
|
9
|
-
previewToken: route.query.token
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
});
|
|
File without changes
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { defineGraphqlServerOptions } from "@wpnuxt/core/server-options";
|
|
2
|
-
import { getHeader, getCookie } from "h3";
|
|
3
|
-
import { useRuntimeConfig } from "#imports";
|
|
4
|
-
export default defineGraphqlServerOptions({
|
|
5
|
-
async serverFetchOptions(event, _operation, _operationName, _context) {
|
|
6
|
-
let authorization = getHeader(event, "authorization") || "";
|
|
7
|
-
if (!authorization) {
|
|
8
|
-
const config = useRuntimeConfig().public.wpNuxtAuth;
|
|
9
|
-
const cookieName = config?.cookieName || "wpnuxt-auth-token";
|
|
10
|
-
const authToken = getCookie(event, cookieName);
|
|
11
|
-
if (authToken) {
|
|
12
|
-
authorization = `Bearer ${authToken}`;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
return {
|
|
16
|
-
headers: {
|
|
17
|
-
// Forward WordPress auth cookies for previews
|
|
18
|
-
Cookie: getHeader(event, "cookie") || "",
|
|
19
|
-
// Forward authorization header or token from cookie
|
|
20
|
-
Authorization: authorization
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
},
|
|
24
|
-
async onServerError(event, error, _operation, operationName) {
|
|
25
|
-
const url = event.node.req.url || "unknown";
|
|
26
|
-
console.error(`[WPNuxt] GraphQL error in ${operationName} (${url}):`, error.message);
|
|
27
|
-
}
|
|
28
|
-
});
|
/package/dist/runtime/{app/graphqlMiddleware.clientOptions.d.ts → internal/graphql-client.d.ts}
RENAMED
|
File without changes
|