redocly-cli icon indicating copy to clipboard operation
redocly-cli copied to clipboard

Add option `--prefix-components-with-filepath` to `join`

Open uncaught opened this issue 1 year ago • 2 comments

Is your feature request related to a problem? Please describe.

This is a follow-up to #1566:

When you have nested component references, the names of the components can quickly collide.

Currently the only options are:

  • uniquely name all components (which is a big code smell in my opinion - the file is already a namespace)
  • or you use --prefix-components-with-info-prop, which can blow up your joined yaml by duplicated components. (I suppose this will grow exponentially with each layer of referenced file.)

Describe the solution you'd like

I would like to get a new option in, lets call it --prefix-components-with-filepath that has three values:

  • auto: (default) will optionally prefix the components with pieces of the filename + path until it is unique
  • name: will always prefix the components with the filename and then optionally add parts of the path until it is unique
  • full: will always prefix the component with the filename plus its path up to the project's root

Technically, the file paths of each component name should be resolved to their realpath, and then cut off starting at the root until uniqueness of the prefix is compromised. This should avoid confusion when different folder depths are involved.

So imagine in all my join sources I'm referencing these components:

  • my/schema/a/foo.yml#/definitions/getRequest
  • my/schema/b/foo.yml#/definitions/getRequest
  • my/bar.yml#/definitions/getRequest
  • my/schema/a/foo.yml#/definitions/postBody
  • my/schema/bar.yml#/definitions/postBody
  • my/schema/a/foo.yml#/definitions/someCompUniqueToAFoo
  • my/schema/b/foo.yml#/definitions/someCompUniqueToBFoo

The resulting component names would be as follow:

  • --prefix-components-with-filepath auto:
    • schema_a_foo_getRequest
    • schema_b_foo_getRequest
    • bar_getRequest
    • a_foo_postBody
    • bar_postBody
    • someCompUniqueToAFoo
    • someCompUniqueToBFoo
  • --prefix-components-with-filepath name:
    • schema_a_foo_getRequest
    • schema_b_foo_getRequest
    • bar_getRequest
    • a_foo_postBody
    • bar_postBody
    • foo_someCompUniqueToAFoo
    • foo_someCompUniqueToBFoo
  • --prefix-components-with-filepath full:
    • my_schema_a_foo_getRequest
    • my_schema_b_foo_getRequest
    • my_bar_getRequest
    • my_schema_a_foo_postBody
    • my_schema_bar_postBody
    • my_schema_a_foo_someCompUniqueToAFoo
    • my_schema_b_foo_someCompUniqueToBFoo

uncaught avatar May 31 '24 16:05 uncaught

@uncaught there's one more possible option you can try to work around that: writing a custom decorator to remove duplicated components in the joined file. You may start with something like the following and improve is as you need:

module.exports = {
  id: "my-local-plugin",
  decorators: {
    oas3: {
      'remove-duplicated-schemas': () => {
        const refs = {};
        const schemasToDelete = [];
        return {
          ref: {
            enter(ref, ctx) {
              if (!ref.$ref.startsWith("#/components/schemas/")) {
                // Ignore refs that are not pointing to a schema inside the current file
                return;
              }
              const stringifiedSchema = JSON.stringify(ctx.resolve(ref).node);
              if (refs[stringifiedSchema]) {
                const [_, schemaName] = ref.$ref.split("#/components/schemas/");
                schemasToDelete.push(schemaName);
                ref.$ref = refs[stringifiedSchema];
              } else {
                refs[stringifiedSchema] = ref.$ref;
              }
            },
          },
          NamedSchemas: {
            leave(namedSchemas) {
              for (const schemaName of schemasToDelete) {
                delete namedSchemas[schemaName];
              }
            },
          },
        };
      },
    },
  },
};

The command then will look like this: redocly join RouteA.yml RouteB.yml --prefix-components-with-info-prop title -o joined.yaml && redocly bundle joined.yaml.

tatomyr avatar Jun 03 '24 11:06 tatomyr

Thank you @uncaught for the feature request, we will certainly consider it. And thank you @tatomyr for adding the workaround that can be used with the current version of the tool - I'm sure this will help someone!

lornajane avatar Jun 04 '24 08:06 lornajane