Skip to content

Commit f6bc3b1

Browse files
committed
feat: flexible id field name
1 parent 0d090a0 commit f6bc3b1

File tree

14 files changed

+305
-69
lines changed

14 files changed

+305
-69
lines changed

eslint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ export default eslintToolingTs.config(
121121
{
122122
name: 'package/unicorn-overrides',
123123
rules: {
124+
'unicorn/consistent-destructuring': 'off',
124125
'unicorn/prevent-abbreviations': [
125126
'warn',
126127
{ allowList: { fn: true, args: true } },
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
directive @delete on OBJECT
2+
3+
type Post @delete {
4+
x1: ID
5+
x2: ID!
6+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
directive @delete on OBJECT
2+
3+
type Post @delete

src/macros/delete/index.ts

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Bundle } from '#package/document.js'
22
import type { DefinitionNode } from 'graphql'
33
import type { Document } from '#package/document.js'
4+
import { invoke } from '@txe/invoke'
45
import { Kind } from 'graphql'
56
import type { ObjectTypeDefinitionNode } from 'graphql'
67

@@ -78,31 +79,53 @@ function addMutation(node: ObjectTypeDefinitionNode) {
7879
},
7980
],
8081
},
81-
{
82-
kind: Kind.INPUT_OBJECT_TYPE_DEFINITION,
83-
name: {
84-
kind: Kind.NAME,
85-
value: `Delete${node.name.value}Input`,
86-
},
87-
fields: [
82+
...invoke(() => {
83+
// oxlint-disable-next-line typescript-eslint(no-non-null-assertion)
84+
const idFields = node.fields!.filter((field) => {
85+
let { type } = field
86+
87+
if (type.kind === Kind.NON_NULL_TYPE) {
88+
// eslint-disable-next-line prefer-destructuring
89+
type = type.type
90+
}
91+
92+
return type.kind === Kind.NAMED_TYPE && type.name.value === 'ID'
93+
})
94+
95+
if (!(idFields.length === 1 && idFields[0] !== undefined)) {
96+
throw new Error(
97+
'Type with directive "@delete" should have exactly one field of type ID.',
98+
)
99+
}
100+
101+
return [
88102
{
89-
kind: Kind.INPUT_VALUE_DEFINITION,
103+
kind: Kind.INPUT_OBJECT_TYPE_DEFINITION,
90104
name: {
91105
kind: Kind.NAME,
92-
value: 'id',
106+
value: `Delete${node.name.value}Input`,
93107
},
94-
type: {
95-
kind: Kind.NON_NULL_TYPE,
96-
type: {
97-
kind: Kind.NAMED_TYPE,
108+
fields: [
109+
{
110+
kind: Kind.INPUT_VALUE_DEFINITION,
98111
name: {
99112
kind: Kind.NAME,
100-
value: 'ID',
113+
value: idFields[0]?.name.value,
114+
},
115+
type: {
116+
kind: Kind.NON_NULL_TYPE,
117+
type: {
118+
kind: Kind.NAMED_TYPE,
119+
name: {
120+
kind: Kind.NAME,
121+
value: 'ID',
122+
},
123+
},
101124
},
102125
},
103-
},
126+
],
104127
},
105-
],
106-
},
128+
]
129+
}),
107130
]
108131
}

src/macros/delete/unit.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,33 @@ test('expand directive @create', async () => {
1919

2020
expect(result).toBe(schema.expanded)
2121
})
22+
23+
test('@delete error case 1', async () => {
24+
const schema = await importDefaults({
25+
initial: () => import('./fixtures/errors/x1.gql?raw'),
26+
})
27+
28+
expect(() => buildSchema(schema.initial)).not.toThrow()
29+
30+
// oxlint-disable-next-line eslint-plugin-jest(no-conditional-expect)
31+
await expect(
32+
execExpansion({ expand, schema: schema.initial }),
33+
).rejects.toThrow(
34+
'Type with directive "@delete" should have exactly one field of type ID.',
35+
)
36+
})
37+
38+
test('@delete error case 2', async () => {
39+
const schema = await importDefaults({
40+
initial: () => import('./fixtures/errors/x2.gql?raw'),
41+
})
42+
43+
expect(() => buildSchema(schema.initial)).not.toThrow()
44+
45+
// oxlint-disable-next-line eslint-plugin-jest(no-conditional-expect)
46+
await expect(
47+
execExpansion({ expand, schema: schema.initial }),
48+
).rejects.toThrow(
49+
'Type with directive "@delete" should have exactly one field of type ID.',
50+
)
51+
})
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
directive @item on OBJECT
2+
3+
type Post @item {
4+
x1: ID
5+
x2: ID!
6+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
directive @item on OBJECT
2+
3+
type Post @item

src/macros/item/index.ts

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -80,31 +80,53 @@ function addMutation(node: ObjectTypeDefinitionNode) {
8080
},
8181
],
8282
},
83-
{
84-
kind: Kind.INPUT_OBJECT_TYPE_DEFINITION,
85-
name: {
86-
kind: Kind.NAME,
87-
value: `${node.name.value}ItemInput`,
88-
},
89-
fields: [
83+
...invoke(() => {
84+
// oxlint-disable-next-line typescript-eslint(no-non-null-assertion)
85+
const idFields = node.fields!.filter((field) => {
86+
let { type } = field
87+
88+
if (type.kind === Kind.NON_NULL_TYPE) {
89+
// eslint-disable-next-line prefer-destructuring
90+
type = type.type
91+
}
92+
93+
return type.kind === Kind.NAMED_TYPE && type.name.value === 'ID'
94+
})
95+
96+
if (!(idFields.length === 1 && idFields[0] !== undefined)) {
97+
throw new Error(
98+
'Type with directive "@item" should have exactly one field of type ID.',
99+
)
100+
}
101+
102+
return [
90103
{
91-
kind: Kind.INPUT_VALUE_DEFINITION,
104+
kind: Kind.INPUT_OBJECT_TYPE_DEFINITION,
92105
name: {
93106
kind: Kind.NAME,
94-
value: 'id',
107+
value: `${node.name.value}ItemInput`,
95108
},
96-
type: {
97-
kind: Kind.NON_NULL_TYPE,
98-
type: {
99-
kind: Kind.NAMED_TYPE,
109+
fields: [
110+
{
111+
kind: Kind.INPUT_VALUE_DEFINITION,
100112
name: {
101113
kind: Kind.NAME,
102-
value: 'ID',
114+
value: idFields[0]?.name.value,
115+
},
116+
type: {
117+
kind: Kind.NON_NULL_TYPE,
118+
type: {
119+
kind: Kind.NAMED_TYPE,
120+
name: {
121+
kind: Kind.NAME,
122+
value: 'ID',
123+
},
124+
},
103125
},
104126
},
105-
},
127+
],
106128
},
107-
],
108-
},
129+
]
130+
}),
109131
]
110132
}

src/macros/item/unit.spec.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { expect } from 'vitest'
55
import { importDefaults } from '#package/testing/import-defaults.js'
66
import { test } from 'vitest'
77

8-
test('expand directive @create', async () => {
8+
test('expand directive @item', async () => {
99
const schema = await importDefaults({
1010
base: () => import('#package/fixtures/base.gql?raw'),
1111
initial: () => import('./fixtures/initial.gql?raw'),
@@ -19,3 +19,33 @@ test('expand directive @create', async () => {
1919

2020
expect(result).toBe(schema.expanded)
2121
})
22+
23+
test('@item error case 1', async () => {
24+
const schema = await importDefaults({
25+
initial: () => import('./fixtures/errors/x1.gql?raw'),
26+
})
27+
28+
expect(() => buildSchema(schema.initial)).not.toThrow()
29+
30+
// oxlint-disable-next-line eslint-plugin-jest(no-conditional-expect)
31+
await expect(
32+
execExpansion({ expand, schema: schema.initial }),
33+
).rejects.toThrow(
34+
'Type with directive "@item" should have exactly one field of type ID.',
35+
)
36+
})
37+
38+
test('@item error case 2', async () => {
39+
const schema = await importDefaults({
40+
initial: () => import('./fixtures/errors/x2.gql?raw'),
41+
})
42+
43+
expect(() => buildSchema(schema.initial)).not.toThrow()
44+
45+
// oxlint-disable-next-line eslint-plugin-jest(no-conditional-expect)
46+
await expect(
47+
execExpansion({ expand, schema: schema.initial }),
48+
).rejects.toThrow(
49+
'Type with directive "@item" should have exactly one field of type ID.',
50+
)
51+
})
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
directive @update on OBJECT
2+
3+
type Post @update {
4+
x1: ID
5+
x2: ID!
6+
}

0 commit comments

Comments
 (0)