Add option `--prefix-components-with-filepath` to `join`
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 uniquename: will always prefix the components with the filename and then optionally add parts of the path until it is uniquefull: 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/getRequestmy/schema/b/foo.yml#/definitions/getRequestmy/bar.yml#/definitions/getRequestmy/schema/a/foo.yml#/definitions/postBodymy/schema/bar.yml#/definitions/postBodymy/schema/a/foo.yml#/definitions/someCompUniqueToAFoomy/schema/b/foo.yml#/definitions/someCompUniqueToBFoo
The resulting component names would be as follow:
--prefix-components-with-filepath auto:schema_a_foo_getRequestschema_b_foo_getRequestbar_getRequesta_foo_postBodybar_postBodysomeCompUniqueToAFoosomeCompUniqueToBFoo
--prefix-components-with-filepath name:schema_a_foo_getRequestschema_b_foo_getRequestbar_getRequesta_foo_postBodybar_postBodyfoo_someCompUniqueToAFoofoo_someCompUniqueToBFoo
--prefix-components-with-filepath full:my_schema_a_foo_getRequestmy_schema_b_foo_getRequestmy_bar_getRequestmy_schema_a_foo_postBodymy_schema_bar_postBodymy_schema_a_foo_someCompUniqueToAFoomy_schema_b_foo_someCompUniqueToBFoo
@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.
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!