Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON Field - jsonSchema unable to generate types nor parse $ref correctly - uses payload-types.ts as target #11383

Open
greenlover1991 opened this issue Feb 25, 2025 · 0 comments
Labels
status: needs-triage Possible bug which hasn't been reproduced yet

Comments

@greenlover1991
Copy link

Describe the Bug

I have this jsonSchema that uses $ref (recursively) to validate a JSON field

JSON Schema
// src/collections/Posts/index.ts
import type { JSONField } from 'payload';

type JSONSchema = Exclude<JSONField['jsonSchema'], undefined>['schema'];
const filterSchema: JSONSchema = {
  $schema: 'http://json-schema.org/draft-07/schema#',
  type: 'object',
  definitions: {
    condition: {
      type: 'object',
      properties: {
        _int_eq: {
          type: 'object',
          additionalProperties: { type: 'integer' },
        },
        _str_in: {
          type: 'object',
          additionalProperties: {
            type: 'array',
            items: { type: 'string' },
          },
        },
        _str_not_in: {
          type: 'object',
          additionalProperties: {
            type: 'array',
            items: { type: 'string' },
          },
        },
        _str_eq: {
          type: 'object',
          additionalProperties: { type: 'string' },
        },
        _bool_eq: {
          type: 'object',
          additionalProperties: { type: 'boolean' },
        },
        _str_not_eq: {
          type: 'object',
          additionalProperties: { type: 'string' },
        },
        _int_in_range_exc: {
          type: 'object',
          additionalProperties: {
            type: 'object',
            properties: {
              min: { type: 'integer' },
              max: { type: 'integer' },
            },
            anyOf: [{ required: ['min'] }, { required: ['max'] }],
            additionalProperties: false,
          },
        },
        _array_contains: {
          type: 'object',
          additionalProperties: {
            anyOf: [
              { type: 'string' },
              { type: 'number' },
              { type: 'integer' },
              { type: 'boolean' },
            ],
          },
        },
        _array_not_contains: {
          type: 'object',
          additionalProperties: {
            anyOf: [
              { type: 'string' },
              { type: 'number' },
              { type: 'integer' },
              { type: 'boolean' },
            ],
          },
        },
        _int_in_range_inc: {
          type: 'object',
          additionalProperties: {
            type: 'object',
            properties: {
              min: { type: 'integer' },
              max: { type: 'integer' },
            },
            anyOf: [{ required: ['min'] }, { required: ['max'] }],
            additionalProperties: false,
          },
        },
        _is_null: {
          type: 'object',
          additionalProperties: { type: 'boolean' },
        },
        _is_not_null: {
          type: 'object',
          additionalProperties: { type: 'boolean' },
        },
        _days_from_now: {
          type: 'object',
          additionalProperties: { type: 'integer', minimum: 0 },
        },
        _or: {
          type: 'array',
          items: { $ref: '#/definitions/condition' },
        },
        _and: {
          type: 'array',
          items: { $ref: '#/definitions/condition' },
        },
      },
      additionalProperties: false,
    },
  },
  properties: {
    _or: {
      type: 'array',
      items: { $ref: '#/definitions/condition' },
    },
    _and: {
      type: 'array',
      items: { $ref: '#/definitions/condition' },
    },
  },
  additionalProperties: false,
};

export const Posts: CollectionConfig<'posts'> = {
  // ...snipped
  fields: [
    {
      name: 'criteria_json',
      type: 'json',
      jsonSchema: {
        uri: 'dummy://foo.json',
        fileMatch: ['dummy://foo.json'],
        schema: filterSchema,
      },
    },
  ]
}

And during editing, the browser is able to autosuggest and validate the JSON field correctly:

Image


But when I run pnpm generate:types, I get this error:

generate:types Error
  $ pnpm generate:types
  
  > [email protected] generate:types /workspaces/my-monorepo-project/javascript/apps/payload-website
  > cross-env NODE_OPTIONS=--no-deprecation payload generate:types
  
  [05:47:24] INFO: Compiling TS types for Collections and Globals...
  node:internal/process/promises:391
      triggerUncaughtException(err, true /* fromPromise */);
      ^
  
  JSONParserError: Missing $ref pointer "#/definitions/condition". Token "condition" does not exist.
      at Pointer.resolve (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@[email protected]/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/pointer.js:118:23)
      at $Ref.resolve (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@[email protected]/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/ref.js:81:28)
      at $Refs._resolve (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@[email protected]/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/refs.js:168:21)
      at dereference$Ref (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@[email protected]/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:191:27)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@[email protected]/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:105:40)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@[email protected]/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@[email protected]/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@[email protected]/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@[email protected]/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@[email protected]/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44) {
    code: 'EMISSINGPOINTER',
    name: 'MissingPointerError',
    source: '/workspaces/my-monorepo-project/javascript/apps/payload-website/',
    path: null,
    toJSON: [Function: toJSON],
    targetToken: 'condition',
    targetRef: '#/definitions/condition',
    targetFound: '#/definitions',
    parentPath: '#/definitions/posts/properties/testJson/definitions/condition/properties/_or/items',
    [Symbol(nodejs.util.inspect.custom)]: [Function: inspect]

This worked before in Payload V2 and I did not have to explicitly specify the JSONSchema type as well.


If i change the $ref

from:

  $ref: '#/definitions/condition'

to:

  $ref: '#/definitions/posts/properties/criteria_json/definitions/condition'

then it's able to generate the types:

JSON field with schema - generated types ```typescript // payload-types.ts export interface Post { // ...snip criteria_json?: { _or?: Condition[]; _and?: Condition[]; }; }
export interface Condition {
  _int_eq?: {
    [k: string]: number;
  };
  _str_in?: {
    [k: string]: string[];
  };
  _str_not_in?: {
    [k: string]: string[];
  };
  _str_eq?: {
    [k: string]: string;
  };
  // ...snipped...
}
</details>

But in the browser, when editing the JSON field, it does not suggest / validate based on the schema:

![Image](https://github.com/user-attachments/assets/3bf5baaf-945d-4239-8c92-76fed8b3750b)
![Image](https://github.com/user-attachments/assets/d932b2d7-849a-407a-b765-ef58b617e651)

----

Troubleshooting this, it seems that the relative path being used for the $ref is the `payload-types.ts`? If I specify an external schema file as the `jsonSchema.uri`, then it seems to work in both the generation of types and in the browser field editor. But I don't want to host the external JSON schema file separately.

-----

Expected behavior: should be able to generate types with JSON field with JSON schema using $ref

### Link to the code that reproduces this issue

https://github.com/payloadcms/payload/blob/main/packages/payload/src/bin/generateTypes.ts#L32

### Reproduction Steps

1. Create new project `pnpx create-payload-app@latest -t website`
2. Add JSON field to `src/collections/Posts/index.tsx`
3. Specify a local jsonSchema using `$ref` (same as in the ticket description)
4. Run `pnpm generate:types`
5. [BUG] Unable to generate the types, and console will show error.

### Which area(s) are affected? (Select all that apply)

area: core

### Environment Info

```text
Binaries:
Node: 20.18.2
npm: N/A
Yarn: N/A
pnpm: 9.14.2
Relevant Packages:
payload: 3.23.0
Operating System:
Platform: linux
Arch: x64
Version: #1 SMP Tue Jan 21 10:23:32 UTC 2025
Available memory (MB): 27015
Available CPU cores: 11
@greenlover1991 greenlover1991 added status: needs-triage Possible bug which hasn't been reproduced yet validate-reproduction labels Feb 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs-triage Possible bug which hasn't been reproduced yet
Projects
None yet
Development

No branches or pull requests

1 participant