bcherny / json-schema-to-typescript

Compile JSON Schema to TypeScript type declarations
https://bcherny.github.io/json-schema-to-typescript-browser/
MIT License
2.9k stars 387 forks source link

Maximum call stack size exceeded #482

Open mattem opened 2 years ago

mattem commented 2 years ago

When running against the CircleCI configuration schema here, https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/circleciconfig.json a max call stack size error is thrown.

This seems to be caused by the circular references on the logical definitions within the schema.

Using version 11.0.2 of json-schema-to-typescript

[
  RangeError: Maximum call stack size exceeded
      at memoized (/private/var/tmp/_bazel_matt/1074e32c37c3b3e7bc10b426cb247bb7/sandbox/darwin-sandbox/129/execroot/__main__/bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/rosetta/src/types/circleci_dts__js_binary.sh.runfiles/__main__/node_modules/.aspect_rules_js/lodash@4.17.21/node_modules/lodash/lodash.js:10612:30)
      at /private/var/tmp/_bazel_matt/1074e32c37c3b3e7bc10b426cb247bb7/sandbox/darwin-sandbox/129/execroot/__main__/bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/rosetta/src/types/circleci_dts__js_binary.sh.runfiles/__main__/node_modules/.aspect_rules_js/json-schema-to-typescript@11.0.2/node_modules/json-schema-to-typescript/dist/src/generator.js:260:81
      at Array.map (<anonymous>)
      at generateSetOperation (/private/var/tmp/_bazel_matt/1074e32c37c3b3e7bc10b426cb247bb7/sandbox/darwin-sandbox/129/execroot/__main__/bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/rosetta/src/types/circleci_dts__js_binary.sh.runfiles/__main__/node_modules/.aspect_rules_js/json-schema-to-typescript@11.0.2/node_modules/json-schema-to-typescript/dist/src/generator.js:260:30)
      at generateRawType (/private/var/tmp/_bazel_matt/1074e32c37c3b3e7bc10b426cb247bb7/sandbox/darwin-sandbox/129/execroot/__main__/bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/rosetta/src/types/circleci_dts__js_binary.sh.runfiles/__main__/node_modules/.aspect_rules_js/json-schema-to-typescript@11.0.2/node_modules/json-schema-to-typescript/dist/src/generator.js:249:20)
      at generateTypeUnmemoized (/private/var/tmp/_bazel_matt/1074e32c37c3b3e7bc10b426cb247bb7/sandbox/darwin-sandbox/129/execroot/__main__/bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/rosetta/src/types/circleci_dts__js_binary.sh.runfiles/__main__/node_modules/.aspect_rules_js/json-schema-to-typescript@11.0.2/node_modules/json-schema-to-typescript/dist/src/generator.js:143:16)
      at memoized (/private/var/tmp/_bazel_matt/1074e32c37c3b3e7bc10b426cb247bb7/sandbox/darwin-sandbox/129/execroot/__main__/bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/rosetta/src/types/circleci_dts__js_binary.sh.runfiles/__main__/node_modules/.aspect_rules_js/lodash@4.17.21/node_modules/lodash/lodash.js:10620:27)
      at /private/var/tmp/_bazel_matt/1074e32c37c3b3e7bc10b426cb247bb7/sandbox/darwin-sandbox/129/execroot/__main__/bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/rosetta/src/types/circleci_dts__js_binary.sh.runfiles/__main__/node_modules/.aspect_rules_js/json-schema-to-typescript@11.0.2/node_modules/json-schema-to-typescript/dist/src/generator.js:271:72
      at Array.map (<anonymous>)
      at generateInterface (/private/var/tmp/_bazel_matt/1074e32c37c3b3e7bc10b426cb247bb7/sandbox/darwin-sandbox/129/execroot/__main__/bazel-out/darwin_arm64-opt-exec-2B5CBBC6/bin/rosetta/src/types/circleci_dts__js_binary.sh.runfiles/__main__/node_modules/.aspect_rules_js/json-schema-to-typescript@11.0.2/node_modules/json-schema-to-typescript/dist/src/generator.js:269:14)
]
wawhal commented 12 months ago

I'm facing the same. Any update on this?

EDIT: My issue was that it was getting into circular references because two types (one of which was included through a remote JSON schema) had the same definition name. I just added different titles to them and it worked fine for me.

yorinasub17 commented 9 months ago

I also ran into this and traced it down to the minimum reproducible example:

export const input = {
  definitions: {
    logical: {
      oneOf: [
        {
          type: 'string',
          description: 'oneOf string',
        },
        {
          type: 'object',
          description: 'oneOf object',
          properties: {
            not: {
              description: 'THIS IS PROBLEMATIC',
              $ref: '#/definitions/logical',
            },
          },
        },
      ],
    },
  },
  properties: {
    workflows: {
      type: 'object',
      description: 'workflows object',
      properties: {
        when: {
          description: 'workflows when',
          $ref: '#/definitions/logical',
        },
      },
    },
  },
  type: 'object',
}

After experimenting with the test suite, I identified that the issue is that when the recursive reference has a description, the code can not identify a standaloneName for the definition, and thus it ends up recursing in the generateType function.

However, if the description field is dropped from the recursive reference (the one set to THIS IS PROBLEMATIC), then it can generate without hitting the max callstack. I am not sure why that is the case, or how to fix it, but at least that appears to be what is causing the issue.

Note that it also works if the top level logical definition object has a title, which makes sense since that also sets a standaloneName on the object.


For anyone who wants to workaround this on the circleci config schema, you can do the following:

import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";

import { compile } from "json-schema-to-typescript";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const resp = await fetch("https://json.schemastore.org/circleciconfig.json");
const schema = await resp.json();

// Update the id, since it is used to generate the base type.
schema["$id"] = "CircleCIConfig";

// Assist json-schema-to-typescript by giving the problematic definition a title.
schema.definitions.logical.title = "logical";

const types = await compile(schema, "CircleCIConfig", {
  strictIndexSignatures: true,
});
fs.writeFileSync(path.join(__dirname, "circleci.d.ts"), types);

Note however that although this runs generation to completion, the resulting code is not compilable as it runs into https://github.com/bcherny/json-schema-to-typescript/issues/559 , so that patch is also required to get the circleci config to work.