Skip to content

Commit 004e17e

Browse files
committed
feat: improve route queries, types
1 parent 06f0b48 commit 004e17e

File tree

15 files changed

+163
-49
lines changed

15 files changed

+163
-49
lines changed

package-lock.json

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

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vuepal",
3-
"version": "3.0.0-alpha.1",
3+
"version": "3.0.0-alpha.2",
44
"description": "Nuxt+Vue Drupal Integration",
55
"license": "MIT",
66
"homepage": "https://github.com/liip/vuepal",
@@ -50,8 +50,8 @@
5050
"dependencies": {
5151
"change-case": "^5.4.4",
5252
"fzf": "^0.5.2",
53-
"nuxt-graphql-middleware": "^5.0.0-alpha.19",
54-
"nuxt-language-negotiation": "^2.0.0-alpha.1",
53+
"nuxt-graphql-middleware": "^5.0.0-alpha.20",
54+
"nuxt-language-negotiation": "^2.0.0-alpha.2",
5555
"yaml": "^2.4.1"
5656
},
5757
"devDependencies": {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
fragment nodeContact on NodeContact {
2+
__typename
3+
title
4+
uuid
5+
}
6+
7+
fragment nodePressRelease on NodePressRelease {
8+
__typename
9+
title
10+
uuid
11+
date: fieldDate {
12+
formatted(drupalDateFormat: OLIVERO_MEDIUM)
13+
}
14+
lead: fieldLead
15+
contact: fieldContact {
16+
name: label
17+
jobTitle: fieldJobTitle
18+
mail: fieldEmail
19+
}
20+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ import { useDrupalRouteQuery, useRoute } from '#imports'
99
1010
const route = useRoute()
1111
12-
const { entity } = await useDrupalRouteQuery('routeNodeCanonical')
12+
const { entity } = await useDrupalRouteQuery('nodeCanonical')
1313
</script>

playground/app/pages/contact/index.vue

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@ import { definePageMeta } from '#imports'
1010
definePageMeta({
1111
name: 'contact-page',
1212
drupalFrontendRoute: true,
13+
path: '/kontakt',
1314
languageMapping: {
14-
de: '/de/kontakt',
15-
fr: '/fr/contactez-nous',
16-
en: '/en/contact-us',
17-
it: '/it/conatactio',
15+
en: '/contact-us',
1816
},
1917
})
2018
</script>

playground/app/pages/search.vue

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ import { definePageMeta } from '#imports'
88
definePageMeta({
99
name: 'search',
1010
drupalFrontendRoute: true,
11+
path: '/suche',
1112
languageMapping: {
12-
de: '/de/suche',
13-
fr: '/fr/chercher',
14-
en: '/en/search',
13+
en: '/search',
1514
},
1615
})
1716
</script>

playground/nuxt.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export default defineNuxtConfig({
7979
enabled: true,
8080
routeQueries: {
8181
nodeCanonical: {
82-
fragments: ['nodePage'],
82+
fragments: ['nodePage'] as never[],
8383
},
8484
},
8585
},

src/build/classes/ModuleHelper.ts

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -96,20 +96,6 @@ export class ModuleHelper {
9696
}
9797
})
9898

99-
// Gather all aliases for each layer.
100-
const layerAliases = nuxt.options._layers.map((layer) => {
101-
// @see https://nuxt.com/docs/api/nuxt-config#alias
102-
return {
103-
'~~': layer.config.rootDir,
104-
'@@': layer.config.rootDir,
105-
'~': layer.config.srcDir,
106-
'@': layer.config.srcDir,
107-
// Merge any additional aliases defined by the layer.
108-
// Must be last so that the layer may override the "default" aliases.
109-
...(layer.config.alias || {}),
110-
}
111-
})
112-
11399
// Resolver for the root directory.
114100
const srcResolver = createResolver(nuxt.options.srcDir)
115101
const rootResolver = createResolver(nuxt.options.rootDir)
@@ -240,7 +226,7 @@ export class ModuleHelper {
240226
public addTemplate(
241227
path: string,
242228
build?: (() => string) | null,
243-
buildTypes?: (() => string) | null,
229+
buildTypes?: (() => string | (() => string)) | null,
244230
) {
245231
if (build) {
246232
const content = build().trim()
@@ -258,13 +244,14 @@ export class ModuleHelper {
258244
if (path.startsWith('/')) {
259245
throw new Error('buildTypes is not available for absolute paths.')
260246
}
261-
const content = buildTypes().trim()
247+
const result = buildTypes()
248+
const getContents = typeof result === 'string' ? () => result : result
262249
const filename = 'vuepal-build/' + path + '.d.ts'
263250
addTypeTemplate(
264251
{
265252
filename: filename as `${string}.d.ts`,
266253
write: true,
267-
getContents: () => content,
254+
getContents,
268255
},
269256
{
270257
nuxt: true,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { FragmentDefinitionNode, GraphQLInterfaceType } from 'graphql'
2+
import type { ModuleContext } from 'nuxt-graphql-middleware/utils'
3+
import { pascalCase } from 'change-case'
4+
5+
export class RouteQueryBuilder {
6+
private entityFragments = new Set<string>()
7+
private typesImplementingEntity: Set<string>
8+
9+
constructor(graphql: ModuleContext) {
10+
const entityInterface = graphql.schemaGetType(
11+
'Entity',
12+
) as GraphQLInterfaceType
13+
const result = graphql.getSchema().getImplementations(entityInterface)
14+
15+
this.typesImplementingEntity = new Set(
16+
[...result.interfaces, ...result.objects].map((v) => v.name),
17+
)
18+
}
19+
20+
public handleSchema() {}
21+
22+
public handleFragments(fragments: FragmentDefinitionNode[]) {
23+
this.entityFragments.clear()
24+
for (const fragment of fragments) {
25+
const typeName = fragment.typeCondition.name.value
26+
if (this.typesImplementingEntity.has(typeName)) {
27+
this.entityFragments.add(fragment.name.value)
28+
}
29+
}
30+
}
31+
32+
public buildGraphqlTypesTemplate() {
33+
const fragments = [...this.entityFragments.values()].map((fragmentName) => {
34+
const typeName = pascalCase(fragmentName + 'Fragment')
35+
const line = `"${fragmentName}": ${typeName}`
36+
return { typeName, line }
37+
})
38+
39+
const imports = fragments.map((v) => v.typeName).join(',\n ')
40+
const types = fragments.map((v) => v.line).join(',\n ')
41+
return `
42+
import type {
43+
${imports}
44+
} from '#graphql-operations'
45+
46+
declare module '#vuepal-build/graphql' {
47+
export type EntityFragment = {
48+
${types}
49+
}
50+
}
51+
`.trim()
52+
}
53+
}

src/build/features/drupalRoute/index.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { defineVuepalFeature } from '../defineFeature'
22
import { pascalCase, camelCase } from 'change-case'
3+
import { RouteQueryBuilder } from './RouteQueryBuilder'
4+
import type { EntityFragment } from '#vuepal-build/graphql'
35

46
type DrupalRouteDefinition = {
57
/**
@@ -8,7 +10,7 @@ type DrupalRouteDefinition = {
810
* The fragments must exist in the project and they must target a type
911
* that implements "Entity".
1012
*/
11-
fragments: string[]
13+
fragments: Array<keyof EntityFragment>
1214
}
1315

1416
export default defineVuepalFeature<{
@@ -100,7 +102,10 @@ fragment useDrupalRoute on Query {
100102
`,
101103
)
102104

103-
const routeQueries = Object.entries(options?.routeQueries || {})
105+
const routeQueries = Object.entries(options?.routeQueries || {}) as [
106+
string,
107+
DrupalRouteDefinition,
108+
][]
104109

105110
if (helper.isModuleBuild) {
106111
routeQueries.push([
@@ -170,6 +175,28 @@ declare module '#vuepal-build/route-queries' {
170175
},
171176
)
172177

178+
const routeQueryBuilder = new RouteQueryBuilder(helper.graphql)
179+
180+
const state = {
181+
template: '',
182+
}
183+
184+
helper.addTemplate(
185+
'graphql',
186+
() => {
187+
return 'export {}'
188+
},
189+
() => {
190+
return () => state.template
191+
},
192+
)
193+
194+
helper.nuxt.hook('nuxt-graphql-middleware:build', (ctx) => {
195+
const fragments = ctx.output.getFragments().map((v) => v.node)
196+
routeQueryBuilder.handleFragments(fragments)
197+
state.template = routeQueryBuilder.buildGraphqlTypesTemplate()
198+
})
199+
173200
// Generate GraphQL queries.
174201
routeQueries.forEach(([name, definition]) => {
175202
const queryName = camelCase('route_' + name)

0 commit comments

Comments
 (0)