@wpnuxt/core 2.0.0 → 2.2.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/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wpnuxt/core",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "configKey": "wpNuxt",
5
5
  "compatibility": {
6
6
  "nuxt": ">=4.0.0"
package/dist/module.mjs CHANGED
@@ -8,7 +8,7 @@ import { parse, GraphQLError, visit, print } from 'graphql';
8
8
  import { execSync } from 'node:child_process';
9
9
  import { consola } from 'consola';
10
10
 
11
- const version = "2.0.0";
11
+ const version = "2.2.0";
12
12
 
13
13
  function createModuleError(module, message) {
14
14
  return new Error(formatErrorMessage(module, message));
@@ -190,6 +190,10 @@ function processSelections(selections, level, query, canExtract = true) {
190
190
  if (hasFragments && hasCustomFields) {
191
191
  query.hasInlineFields = true;
192
192
  }
193
+ const fieldNames = selections.filter((s) => s.kind === "Field").map((s) => s.name.value);
194
+ if (fieldNames.includes("pageInfo") && fieldNames.includes("nodes")) {
195
+ query.hasPageInfo = true;
196
+ }
193
197
  selections.forEach((s) => {
194
198
  if (s.kind === "FragmentSpread") {
195
199
  query.fragments?.push(s.name.value.trim());
@@ -254,6 +258,20 @@ async function prepareContext(ctx) {
254
258
  const mutationFnName = (fn) => `useMutation${upperFirst(fn)}`;
255
259
  const formatNodes = (nodes) => nodes?.map((n) => `'${n}'`).join(",") ?? "";
256
260
  const getFragmentType = (q) => {
261
+ if (q.hasPageInfo) {
262
+ if (q.hasInlineFields || !q.fragments?.length) {
263
+ if (q.nodes?.length) {
264
+ let typePath = `${q.name}RootQuery`;
265
+ for (const node of q.nodes) {
266
+ typePath = `NonNullable<${typePath}>['${node}']`;
267
+ }
268
+ typePath = `NonNullable<${typePath}>['nodes'][number]`;
269
+ return typePath;
270
+ }
271
+ return `${q.name}RootQuery`;
272
+ }
273
+ return q.fragments.map((f) => `WithImagePath<${f}Fragment>`).join(" | ");
274
+ }
257
275
  if (q.hasInlineFields || !q.fragments?.length) {
258
276
  if (q.nodes?.length) {
259
277
  let typePath = `${q.name}RootQuery`;
@@ -272,10 +290,16 @@ async function prepareContext(ctx) {
272
290
  };
273
291
  const queryFnExp = (q, typed = false) => {
274
292
  const functionName = fnName(q.name);
293
+ if (q.hasPageInfo) {
294
+ if (!typed) {
295
+ return `export const ${functionName} = (params, options) => useWPConnection('${q.name}', [${formatNodes(q.nodes)}], false, params, options)`;
296
+ }
297
+ return ` export const ${functionName}: (params?: MaybeRefOrGetter<${q.name}QueryVariables>, options?: WPContentOptions) => WPConnectionResult<${getFragmentType(q)}>`;
298
+ }
275
299
  if (!typed) {
276
300
  return `export const ${functionName} = (params, options) => useWPContent('${q.name}', [${formatNodes(q.nodes)}], false, params, options)`;
277
301
  }
278
- return ` export const ${functionName}: (params?: ${q.name}QueryVariables, options?: WPContentOptions) => WPContentResult<${getFragmentType(q)}>`;
302
+ return ` export const ${functionName}: (params?: MaybeRefOrGetter<${q.name}QueryVariables>, options?: WPContentOptions) => WPContentResult<${getFragmentType(q)}>`;
279
303
  };
280
304
  const mutationFnExp = (m, typed = false) => {
281
305
  const functionName = mutationFnName(m.name);
@@ -287,9 +311,14 @@ async function prepareContext(ctx) {
287
311
  ctx.generateImports = () => {
288
312
  const lines = [];
289
313
  const imports = [];
290
- if (queries.length > 0) {
314
+ const hasConnectionQueries = queries.some((q) => q.hasPageInfo);
315
+ const hasContentQueries = queries.some((q) => !q.hasPageInfo);
316
+ if (hasContentQueries) {
291
317
  imports.push("useWPContent");
292
318
  }
319
+ if (hasConnectionQueries) {
320
+ imports.push("useWPConnection");
321
+ }
293
322
  if (mutations.length > 0) {
294
323
  imports.push("useGraphqlMutation");
295
324
  }
@@ -321,7 +350,7 @@ async function prepareContext(ctx) {
321
350
  ctx.generateDeclarations = () => {
322
351
  const declarations = [
323
352
  `import type { ${[...typeSet].join(", ")} } from '#build/graphql-operations'`,
324
- "import type { ComputedRef, Ref } from 'vue'",
353
+ "import type { ComputedRef, MaybeRefOrGetter, Ref } from 'vue'",
325
354
  "import type { AsyncDataRequestStatus } from '#app'",
326
355
  "import type { GraphqlResponse } from 'nuxt-graphql-middleware/types'",
327
356
  "",
@@ -367,6 +396,25 @@ async function prepareContext(ctx) {
367
396
  " status: Ref<AsyncDataRequestStatus>",
368
397
  "}",
369
398
  "",
399
+ "interface WPConnectionResult<T> {",
400
+ " data: ComputedRef<T[] | undefined>",
401
+ " pageInfo: ComputedRef<WPPageInfo | undefined>",
402
+ " loadMore: () => Promise<void>",
403
+ " pending: Ref<boolean>",
404
+ " refresh: () => Promise<void>",
405
+ " execute: () => Promise<void>",
406
+ " clear: () => void",
407
+ " error: Ref<Error | undefined>",
408
+ " status: Ref<AsyncDataRequestStatus>",
409
+ "}",
410
+ "",
411
+ "interface WPPageInfo {",
412
+ " hasNextPage: boolean",
413
+ " hasPreviousPage: boolean",
414
+ " startCursor?: string | null",
415
+ " endCursor?: string | null",
416
+ "}",
417
+ "",
370
418
  "type WPMutationResult<T> = GraphqlResponse<T>",
371
419
  "",
372
420
  "/** Adds relativePath to featuredImage.node when present (injected at runtime by transformData) */",
@@ -945,10 +993,16 @@ const module$1 = defineNuxtModule({
945
993
  configureVercelSettings(nuxt, logger);
946
994
  addImports([
947
995
  { name: "useWPContent", as: "useWPContent", from: resolver.resolve("./runtime/composables/useWPContent") },
996
+ { name: "useWPConnection", as: "useWPConnection", from: resolver.resolve("./runtime/composables/useWPConnection") },
948
997
  { name: "getRelativeImagePath", as: "getRelativeImagePath", from: resolver.resolve("./runtime/util/images") },
949
998
  { name: "isInternalLink", as: "isInternalLink", from: resolver.resolve("./runtime/util/links") },
950
999
  { name: "toRelativePath", as: "toRelativePath", from: resolver.resolve("./runtime/util/links") },
951
- { name: "usePrevNextPost", as: "usePrevNextPost", from: resolver.resolve("./runtime/composables/usePrevNextPost") }
1000
+ { name: "usePrevNextPost", as: "usePrevNextPost", from: resolver.resolve("./runtime/composables/usePrevNextPost") },
1001
+ { name: "isPage", as: "isPage", from: resolver.resolve("./runtime/util/content-type") },
1002
+ { name: "isPost", as: "isPost", from: resolver.resolve("./runtime/util/content-type") },
1003
+ { name: "isContentType", as: "isContentType", from: resolver.resolve("./runtime/util/content-type") },
1004
+ { name: "unwrapScalar", as: "unwrapScalar", from: resolver.resolve("./runtime/util/acf") },
1005
+ { name: "unwrapConnection", as: "unwrapConnection", from: resolver.resolve("./runtime/util/acf") }
952
1006
  // Note: useGraphqlMutation is auto-imported via nuxt-graphql-middleware with includeComposables: true
953
1007
  ]);
954
1008
  addComponentsDir({
File without changes
@@ -0,0 +1,68 @@
1
+ import { useWPContent } from "./useWPContent.js";
2
+ import { computed, ref, toValue, watch as vueWatch } from "#imports";
3
+ export const useWPConnection = (queryName, connectionPath, fixImagePaths, params, options) => {
4
+ const loadMoreCursor = ref(null);
5
+ const isLoadingMore = ref(false);
6
+ const accumulatedItems = ref([]);
7
+ const mergedParams = computed(() => {
8
+ const userParams = toValue(params) ?? {};
9
+ if (loadMoreCursor.value) {
10
+ return { ...userParams, after: loadMoreCursor.value };
11
+ }
12
+ return userParams;
13
+ });
14
+ const result = useWPContent(queryName, connectionPath, fixImagePaths, mergedParams, options);
15
+ const currentNodes = computed(() => {
16
+ const connection = result.data.value;
17
+ if (!connection || typeof connection !== "object") return void 0;
18
+ const nodes = connection.nodes;
19
+ return Array.isArray(nodes) ? nodes : void 0;
20
+ });
21
+ const pageInfo = computed(() => {
22
+ const connection = result.data.value;
23
+ if (!connection || typeof connection !== "object") return void 0;
24
+ return connection.pageInfo;
25
+ });
26
+ vueWatch(currentNodes, (newNodes) => {
27
+ if (!newNodes?.length) return;
28
+ if (isLoadingMore.value) {
29
+ accumulatedItems.value = [...accumulatedItems.value, ...newNodes];
30
+ isLoadingMore.value = false;
31
+ } else {
32
+ accumulatedItems.value = [...newNodes];
33
+ loadMoreCursor.value = null;
34
+ }
35
+ }, { immediate: true });
36
+ const connectionData = computed(() => {
37
+ return accumulatedItems.value.length > 0 ? accumulatedItems.value : currentNodes.value;
38
+ });
39
+ async function loadMore() {
40
+ if (!pageInfo.value?.hasNextPage || !pageInfo.value.endCursor) return;
41
+ isLoadingMore.value = true;
42
+ loadMoreCursor.value = pageInfo.value.endCursor;
43
+ }
44
+ const returnValue = {
45
+ data: connectionData,
46
+ pageInfo,
47
+ loadMore,
48
+ pending: result.pending,
49
+ refresh: async () => {
50
+ accumulatedItems.value = [];
51
+ loadMoreCursor.value = null;
52
+ isLoadingMore.value = false;
53
+ await result.refresh();
54
+ },
55
+ execute: result.execute,
56
+ clear: result.clear,
57
+ error: result.error,
58
+ status: result.status,
59
+ transformError: result.transformError,
60
+ retryCount: result.retryCount,
61
+ isRetrying: result.isRetrying
62
+ };
63
+ const thenable = result;
64
+ return Object.assign(
65
+ thenable.then(() => returnValue),
66
+ returnValue
67
+ );
68
+ };
@@ -1,5 +1,5 @@
1
1
  import { transformData, normalizeUriParam } from "../util/content.js";
2
- import { computed, ref, watch as vueWatch, useAsyncGraphqlQuery, useRuntimeConfig } from "#imports";
2
+ import { computed, ref, toValue, watch as vueWatch, useAsyncGraphqlQuery, useRuntimeConfig } from "#imports";
3
3
  const defaultGetCachedData = (key, app, ctx) => {
4
4
  if (app.isHydrating) {
5
5
  return app.payload.data[key];
@@ -21,7 +21,8 @@ export const useWPContent = (queryName, nodes, fixImagePaths, params, options) =
21
21
  timeout: timeoutOption,
22
22
  ...restOptions
23
23
  } = options ?? {};
24
- const normalizedParams = normalizeUriParam(params);
24
+ const isReactiveParams = typeof params === "function" || params !== null && typeof params === "object" && "__v_isRef" in params;
25
+ const resolvedParams = isReactiveParams ? computed(() => normalizeUriParam(toValue(params)) ?? {}) : normalizeUriParam(params) ?? {};
25
26
  const maxRetries = retryOption === false ? 0 : retryOption ?? 0;
26
27
  const baseRetryDelay = retryDelayOption ?? 1e3;
27
28
  const retryCount = ref(0);
@@ -39,8 +40,12 @@ export const useWPContent = (queryName, nodes, fixImagePaths, params, options) =
39
40
  }, timeoutMs);
40
41
  }
41
42
  const getCachedDataFn = userGetCachedData ?? (clientCache === false ? noCacheGetCachedData : defaultGetCachedData);
43
+ const watchSources = restOptions.watch;
44
+ const autoWatch = isReactiveParams ? [...watchSources ?? [], resolvedParams] : watchSources;
42
45
  const asyncDataOptions = {
43
46
  ...restOptions,
47
+ // Watch reactive params to auto-refetch
48
+ ...autoWatch?.length && { watch: autoWatch },
44
49
  // Our getCachedData that properly checks static.data for SSG
45
50
  getCachedData: getCachedDataFn,
46
51
  // Enable graphql caching so the LRU cache is populated for subsequent navigations
@@ -55,7 +60,7 @@ export const useWPContent = (queryName, nodes, fixImagePaths, params, options) =
55
60
  };
56
61
  const asyncResult = useAsyncGraphqlQuery(
57
62
  String(queryName),
58
- normalizedParams ?? {},
63
+ resolvedParams,
59
64
  asyncDataOptions
60
65
  );
61
66
  const { data, pending, refresh, execute, clear, error, status } = asyncResult;
@@ -10,6 +10,7 @@ import type { NuxtApp } from 'nuxt/app'
10
10
  // Stub for #imports - Vue reactivity & lifecycle
11
11
  export function computed<T>(getter: () => T): ComputedRef<T>
12
12
  export function ref<T>(value: T): Ref<T>
13
+ export function toValue<T>(source: Ref<T> | ComputedRef<T> | (() => T) | T): T
13
14
  export function resolveComponent(name: string): Component | string
14
15
  export function onMounted(callback: () => void): void
15
16
  export function onBeforeUnmount(callback: () => void): void
@@ -29,7 +30,7 @@ export function navigateTo(to: string): Promise<void>
29
30
  // Stub for #imports - nuxt-graphql-middleware
30
31
  export function useAsyncGraphqlQuery<T = unknown>(
31
32
  name: string,
32
- params?: Record<string, unknown>,
33
+ params?: Record<string, unknown> | Ref<Record<string, unknown>> | ComputedRef<Record<string, unknown>>,
33
34
  options?: Record<string, unknown>
34
35
  ): {
35
36
  data: Ref<T | null>
File without changes
@@ -0,0 +1,9 @@
1
+ function unwrapScalar(value) {
2
+ if (value == null) return void 0;
3
+ if (Array.isArray(value)) return value[0];
4
+ return value;
5
+ }
6
+ function unwrapConnection(connection) {
7
+ return connection?.nodes?.[0];
8
+ }
9
+ export { unwrapScalar, unwrapConnection };
File without changes
@@ -0,0 +1,10 @@
1
+ function isPage(node) {
2
+ return node?.contentTypeName === "page";
3
+ }
4
+ function isPost(node) {
5
+ return node?.contentTypeName === "post";
6
+ }
7
+ function isContentType(node, typeName) {
8
+ return node?.contentTypeName === typeName;
9
+ }
10
+ export { isPage, isPost, isContentType };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wpnuxt/core",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "Nuxt module for WordPress integration via GraphQL (WPGraphQL)",
5
5
  "keywords": [
6
6
  "nuxt",