Skip to content

Commit 7a99a1c

Browse files
committed
feat: dynamic useGraphqlRouteQuery
1 parent 9b6e7ff commit 7a99a1c

File tree

12 files changed

+271
-79
lines changed

12 files changed

+271
-79
lines changed

package-lock.json

Lines changed: 24 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"access": "public"
4949
},
5050
"dependencies": {
51+
"change-case": "^5.4.4",
5152
"fzf": "^0.5.2",
5253
"yaml": "^2.4.1"
5354
},
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fragment nodePage on NodePage {
2+
title
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
<div>Template for a page node</div>
3+
</template>

playground/app/pages/[...slug].vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
</template>
66

77
<script lang="ts" setup>
8-
import { useRoute } from '#imports'
8+
import { useDrupalRouteQuery, useRoute } from '#imports'
99
1010
const route = useRoute()
11+
12+
const { entity } = await useDrupalRouteQuery('routeNodeCanonical')
1113
</script>

playground/nuxt.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
export default defineNuxtConfig({
22
ssr: false,
3+
devtools: {
4+
enabled: true,
5+
},
36
modules: [
47
'nuxt-graphql-middleware',
58
'../src/module',
@@ -71,6 +74,11 @@ export default defineNuxtConfig({
7174
},
7275
drupalRoute: {
7376
enabled: true,
77+
routeQueries: {
78+
nodeCanonical: {
79+
fragments: ['nodePage'],
80+
},
81+
},
7482
},
7583
languageSwitchLinks: {
7684
enabled: true,

src/build/features/breadcrumb/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export default defineVuepalFeature({
1111
)
1212

1313
helper.addComposable('useBreadcrumb')
14+
helper.addComponent('VuepalBreadcrumb')
1415
helper.addPlugin('breadcrumb')
1516
helper.addGraphqlFile('fragment.breadcrumb.graphql')
1617
},

src/build/features/drupalRoute/index.ts

Lines changed: 106 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import { defineVuepalFeature } from '../defineFeature'
2+
import { pascalCase, camelCase } from 'change-case'
23

3-
export default defineVuepalFeature({
4+
type DrupalRouteDefinition = {
5+
fragments: string[]
6+
}
7+
8+
export default defineVuepalFeature<{
9+
routeQueries?: Record<string, DrupalRouteDefinition>
10+
}>({
411
name: 'drupalRoute',
512
description: 'Adds routing related GraphQL queries and composables.',
6-
setup(helper) {
13+
setup(helper, options) {
714
helper
815
.assertGraphqlObjectField({ extension: 'routing' }, 'Query', 'route')
916
.assertGraphqlObjectField(
@@ -18,10 +25,15 @@ export default defineVuepalFeature({
1825

1926
helper.addComposable('useDrupalRoute')
2027
helper.addComposable('buildDrupalMetatags')
28+
29+
helper.addGraphqlFile('fragment.metatag.graphql')
30+
31+
// Conditionally add the breadcrumb on the route fragment.
2132
const breadcrumbSpread = helper.hasFeatureEnabled('breadcrumb')
2233
? 'breadcrumb { ...breadcrumb }'
2334
: ''
2435

36+
// Conditionally add the languageSwitchLinks on the route fragment.
2537
const languageSwitchLinksSpread = helper.hasFeatureEnabled(
2638
'languageSwitchLinks',
2739
)
@@ -72,16 +84,101 @@ fragment useDrupalRoute on Query {
7284
}
7385
}
7486
}
87+
`,
88+
)
89+
90+
const routeQueries = Object.entries(options?.routeQueries || {})
91+
92+
if (helper.isModuleBuild) {
93+
routeQueries.push([
94+
'nodeCanonical',
95+
{
96+
fragments: ['nodePage'],
97+
},
98+
])
99+
}
100+
101+
if (!routeQueries.length) {
102+
return
103+
}
104+
105+
helper.addComposable('useDrupalRouteQuery')
75106

76-
fragment metatag on Metatag {
77-
id
78-
tag
79-
attributes {
80-
key
81-
value
107+
helper.addTemplate(
108+
'route-queries',
109+
() => {
110+
const mapping = routeQueries.reduce<Record<string, string>>(
111+
(acc, [name]) => {
112+
acc[name] = camelCase('route_' + name)
113+
return acc
114+
},
115+
{},
116+
)
117+
return `
118+
export const mapping = ${JSON.stringify(mapping, null, 2)}
119+
`
120+
},
121+
() => {
122+
const imports = [
123+
...new Set(routeQueries.flatMap((v) => v[1].fragments)).values(),
124+
]
125+
.map((fragmentName) => {
126+
return pascalCase(fragmentName + 'Fragment')
127+
})
128+
.join(',\n ')
129+
const importStatement = `import type {\n ${imports}\n} from '#graphql-operations'`
130+
const queries = routeQueries
131+
.map(([name, definition]) => {
132+
const fragments = definition.fragments
133+
.map((v) => pascalCase(v + 'Fragment'))
134+
.join(' | ')
135+
return `"${name}": ${fragments}`
136+
})
137+
.join(',\n ')
138+
139+
const possibleQueryNames = routeQueries
140+
.map(([name]) => {
141+
return `'${camelCase('route_' + name)}'`
142+
})
143+
.join(' | ')
144+
145+
return `
146+
${importStatement}
147+
import type { Query } from '#nuxt-graphql-middleware/operation-types'
148+
149+
declare module '#vuepal-build/route-queries' {
150+
export type DrupalRouteQueries = {
151+
${queries}
82152
}
153+
type PossibleQueryNames = ${possibleQueryNames}
154+
export const mapping: Record<keyof DrupalRouteQueries, keyof Pick<Query, PossibleQueryNames>>;
83155
}
84-
`,
156+
`
157+
},
85158
)
159+
160+
// Generate GraphQL queries.
161+
routeQueries.forEach(([name, definition]) => {
162+
const queryName = camelCase('route_' + name)
163+
164+
const spreads = definition.fragments
165+
.map((v) => '...' + v)
166+
.join('\n ')
167+
helper.graphql.addDocument(
168+
'vuepal-route-query:' + name,
169+
`
170+
query ${queryName}($path: String!) {
171+
...useDrupalRoute
172+
route(path: $path) {
173+
... on EntityUrl {
174+
entity {
175+
__typename
176+
${spreads}
177+
}
178+
}
179+
}
180+
}`,
181+
)
182+
})
86183
},
87184
})

src/runtime/composables/useDrupalRoute/index.ts

Lines changed: 2 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { SerializableHead, Link } from '@unhead/vue'
1+
import type { Link } from '@unhead/vue'
22
import type { HookResult } from '@nuxt/schema'
33
import type { RouteLocationRaw } from 'vue-router'
44
import type { UseDrupalRouteFragment } from '#graphql-operations'
@@ -14,6 +14,7 @@ import {
1414
type ComputedRef,
1515
type Ref,
1616
} from '#imports'
17+
import type { DrupalRoute, UseDrupalRoute } from './../../helpers/drupalRoute'
1718

1819
type DrupalRouteMetatags = {
1920
title: string
@@ -30,33 +31,6 @@ type Options = {
3031
noError?: boolean
3132
}
3233

33-
type DrupalRoute = {
34-
/**
35-
* The name of the route, e.g. "entity.node.canonical".
36-
*/
37-
name?: string
38-
39-
/**
40-
* The bundle of the entity, e.g. "page".
41-
*/
42-
entityBundle?: string
43-
44-
/**
45-
* The entity type, e.g. "node".
46-
*/
47-
entityType?: string
48-
49-
/**
50-
* The ID of the entity.
51-
*/
52-
entityId?: string
53-
54-
/**
55-
* The UUID of the entity.
56-
*/
57-
entityUuid?: string
58-
}
59-
6034
type DrupalRouteHookPayload = {
6135
/**
6236
* The Nuxt route path for which the useDrupalRoute composable was called.
@@ -67,23 +41,6 @@ type DrupalRouteHookPayload = {
6741
metatags: DrupalRouteMetatags
6842
}
6943

70-
type UseDrupalRoute<T> = {
71-
/**
72-
* The user-specific fields for the entity, according to their fragment.
73-
*/
74-
entity: ComputedRef<T>
75-
76-
/**
77-
* The Drupal route information.
78-
*/
79-
drupalRoute: ComputedRef<DrupalRoute>
80-
81-
/**
82-
* The mapped meta tags.
83-
*/
84-
metatags: ComputedRef<SerializableHead>
85-
}
86-
8744
type UseDrupalRouteQueryInput =
8845
| UseDrupalRouteFragment
8946
| ComputedRef<UseDrupalRouteFragment | undefined | null>

0 commit comments

Comments
 (0)