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

Add option --prefix-components-with-filepath to join #1575

Open
uncaught opened this issue May 31, 2024 · 2 comments
Open

Add option --prefix-components-with-filepath to join #1575

uncaught opened this issue May 31, 2024 · 2 comments

Comments

@uncaught
Copy link

uncaught commented May 31, 2024

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
@tatomyr
Copy link
Contributor

tatomyr commented Jun 3, 2024

@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.

@lornajane
Copy link
Collaborator

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 lornajane added the p3 label Jun 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants