Skip to content
New issue

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

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

Already on GitHub? # 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

greenlover1991 commented Feb 25, 2025

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
  
  > payload-website@1.0.0 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/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/pointer.js:118:23)
      at $Ref.resolve (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/ref.js:81:28)
      at $Refs._resolve (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/refs.js:168:21)
      at dereference$Ref (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:191:27)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:105:40)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/node_modules/@apidevtools/json-schema-ref-parser/dist/lib/dereference.js:136:44)
      at crawl (/workspaces/my-monorepo-project/javascript/node_modules/.pnpm/@apidevtools+json-schema-ref-parser@11.9.0/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
  // 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...
  }

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

Image
Image


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

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
# for free to join this conversation on GitHub. Already have an account? # 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