Skip to content

Commit cb3a078

Browse files
authored
feat: support external JSON schema file references in type generation (#14749)
## Description Enables the use of external JSON schema file references in type generation by adding the `cwd` parameter to `json-schema-to-typescript`'s `compile()` function. ## Changes - Added `cwd: process.cwd()` parameter to `generateTypes()` compile call - Enables `$ref` pointers to external `.json` schema files in `typescriptSchema` field config - Added test case with external schema file reference - Documented external schema reference usage in generating-types.mdx ## Test Coverage ✅ New test: `resolves external schema file references` - Created `test/types/schemas/custom-type.json` as external schema - Added field with `typescriptSchema` referencing the external file - Verified generated types include the external type correctly ## Example Usage ```typescript // payload.config.ts { typescript: { schema: [ ({ jsonSchema }) => { jsonSchema.definitions.MyType = { $ref: './schemas/my-type.json' } return jsonSchema }, ] } } ``` External references are resolved relative to `process.cwd()`. ## Related Issue This was identified during investigation of JSON Schema parser capabilities. The `cwd` parameter is a standard `json-schema-to-typescript` option that was not being passed.
1 parent 5150388 commit cb3a078

File tree

6 files changed

+60
-0
lines changed

6 files changed

+60
-0
lines changed

docs/typescript/generating-types.mdx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,28 @@ export interface Test {
9898

9999
This function takes the existing JSON schema as an argument and returns the modified JSON schema. It can be useful for plugins that wish to generate their own types.
100100

101+
### External schema references
102+
103+
You can use `$ref` to reference external JSON schema files in your custom schemas:
104+
105+
```ts
106+
// payload.config.ts
107+
{
108+
typescript: {
109+
schema: [
110+
({ jsonSchema }) => {
111+
jsonSchema.definitions.MyType = {
112+
$ref: './schemas/my-type.json',
113+
}
114+
return jsonSchema
115+
},
116+
]
117+
}
118+
}
119+
```
120+
121+
External references are resolved relative to your project's working directory (`process.cwd()`).
122+
101123
## Example Usage
102124

103125
For example, let's look at the following simple Payload Config:

packages/payload/src/bin/generateTypes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ export async function generateTypes(
4444
// If a field defines an interfaceName, it should be included in the generated types
4545
// even if it's not used by another type. Reason: the user might want to use it in their own code.
4646
unreachableDefinitions: true,
47+
// Allow resolving external file references in $ref pointers
48+
cwd: process.cwd(),
4749
})
4850

4951
compiled = addSelectGenericsToGeneratedTypes({ compiledGeneratedTypes: compiled })

test/types/config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,15 @@ export default buildConfigWithDefaults({
7979
},
8080
],
8181
},
82+
{
83+
name: 'externalType',
84+
type: 'text',
85+
typescriptSchema: [
86+
() => ({
87+
$ref: './test/types/schemas/custom-type.json',
88+
}),
89+
],
90+
},
8291
],
8392
},
8493
{

test/types/payload-types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,14 @@ export interface Post {
164164
insideNamedGroup?: string | null;
165165
};
166166
radioField: MyRadioOptions;
167+
externalType?: CustomType;
167168
updatedAt: string;
168169
createdAt: string;
169170
}
171+
export interface CustomType {
172+
externalField: string;
173+
externalNumber?: number;
174+
}
170175
/**
171176
* This interface was referenced by `Config`'s JSON-Schema
172177
* via the `definition` "pages".
@@ -298,6 +303,7 @@ export interface PostsSelect<T extends boolean = true> {
298303
insideNamedGroup?: T;
299304
};
300305
radioField?: T;
306+
externalType?: T;
301307
updatedAt?: T;
302308
createdAt?: T;
303309
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"type": "object",
4+
"properties": {
5+
"externalField": {
6+
"type": "string"
7+
},
8+
"externalNumber": {
9+
"type": "number"
10+
}
11+
},
12+
"required": ["externalField"],
13+
"additionalProperties": false
14+
}

test/types/types.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,13 @@ describe('Types testing', () => {
156156
test('has global generated options interface based on radio field', () => {
157157
expect(asType<Post['radioField']>()).type.toBe<MyRadioOptions>()
158158
})
159+
160+
test('resolves external schema file references', () => {
161+
// The externalType field uses a $ref to ./test/types/schemas/custom-type.json
162+
expect<Post>().type.toHaveProperty('externalType')
163+
expect<NonNullable<Post['externalType']>>().type.toHaveProperty('externalField')
164+
expect<NonNullable<Post['externalType']>>().type.toHaveProperty('externalNumber')
165+
})
159166
})
160167

161168
describe('fields', () => {

0 commit comments

Comments
 (0)