Skip to content

feat: transform path segements to PascalCase in generateOperationId#214

Merged
MathurAditya724 merged 4 commits intorhinobase:mainfrom
matzehecht:fix/operationid-pascalcase
Jan 21, 2026
Merged

feat: transform path segements to PascalCase in generateOperationId#214
MathurAditya724 merged 4 commits intorhinobase:mainfrom
matzehecht:fix/operationid-pascalcase

Conversation

@matzehecht
Copy link
Contributor

this introduces better handling for paths with dashes

paths like /hello-world/foo would result in operationIds like getHelloWorldFoo instead of getHello-worldFoo

this introduces better handling for paths with dashes
@matzehecht matzehecht changed the title feat: transform path segements to pascal case in generateOperationId feat: transform path segements to PascalCase in generateOperationId Jan 14, 2026
@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 19, 2026

Open in StackBlitz

npm i https://pkg.pr.new/rhinobase/hono-openapi@214

commit: 341b6df

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the operation ID generation for OpenAPI paths by transforming path segments to PascalCase, properly handling dashes and other non-word characters. Previously, paths like /hello-world would generate operation IDs like getHello-world, but now they correctly generate getHelloWorld.

Changes:

  • Replaced the simple capitalize function with a more sophisticated toPascalCase function that handles dashes, underscores, and other non-word characters
  • Added test coverage for paths with dashes
  • Updated snapshots to reflect the new operation ID format

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/utils.ts Replaced capitalize function with toPascalCase that properly handles dashes and non-word characters in path segments
src/tests/zodv3.test.ts Added test case for operation ID generation with dashed paths
src/tests/snapshots/zodv3.test.ts.snap Added snapshot for the new test showing the expected getSomePath operation ID

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +186 to +199
const param = z
.object({
id: z.coerce.number(),
})
.openapi({ ref: "Param" });

const app = new Hono().get(
"/some-path",
describeRoute({
tags: ["test"],
summary: "Test route",
description: "This is a test route",
}),
validator("param", param),
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test defines a path parameter validator but the route path '/some-path' doesn't contain any path parameters. The param validator at lines 186-190 expects an 'id' path parameter, but there's no ':id' in the route path. This appears to be copy-pasted from the 'describeResponse' test above. Either remove the param validator or change the path to include a path parameter like '/:id/some-path' to properly test the dash handling in both static and dynamic path segments.

Copilot uses AI. Check for mistakes.
Comment on lines +180 to +230
it("operation id for path with dash", async () => {
const query = z.object({
limit: z.coerce.number().default(10),
offset: z.coerce.number().default(0),
});

const param = z
.object({
id: z.coerce.number(),
})
.openapi({ ref: "Param" });

const app = new Hono().get(
"/some-path",
describeRoute({
tags: ["test"],
summary: "Test route",
description: "This is a test route",
}),
validator("param", param),
validator("query", query),
describeResponse(
async (c) => {
c.req.valid("query");
c.req.valid("param");

return c.json({
result: [],
count: 0,
});
},
{
200: {
description: "Success",
content: {
"application/json": {
vSchema: z.object({
result: z.array(z.string()),
count: z.number(),
}),
},
},
},
},
),
);

const specs = await generateSpecs(app);

expect(specs).toMatchSnapshot();
});
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test only covers a single path segment with a dash. Consider adding test cases for additional scenarios to ensure the toPascalCase function works correctly in all cases, such as: multiple segments with dashes (e.g., '/hello-world/foo-bar'), path parameters with dashes (e.g., '/:user-id'), and combinations of static and dynamic segments with dashes (e.g., '/hello-world/:user-id/foo-bar').

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +186 to +200
const param = z
.object({
id: z.coerce.number(),
})
.openapi({ ref: "Param" });

const app = new Hono().get(
"/some-path",
describeRoute({
tags: ["test"],
summary: "Test route",
description: "This is a test route",
}),
validator("param", param),
validator("query", query),
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test defines a param validator for a path parameter, but the path /some-path contains no path parameters. This creates an inconsistent test case where the validator is declared but cannot be used. Either remove the param validator and the c.req.valid("param") call, or change the path to include a parameter like /some-path/:id to properly test the PascalCase conversion with both dashes and parameters.

Copilot uses AI. Check for mistakes.
Comment on lines +50 to 57
const toPascalCase = (text: string) =>
text
.split(/[\W_]+/)
.filter(Boolean)
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join("");

const generateOperationId = (route: RouterRoute) => {
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The toPascalCase function preserves non-word characters at the beginning of the string due to the (?<!^) negative lookbehind in the regex. This means a path segment like "-test" would be converted to "-Test" instead of "Test", resulting in an invalid operation ID. Consider handling leading non-word characters explicitly by trimming them or including them in the removal pattern.

Copilot uses AI. Check for mistakes.
Resolve merge conflicts in utils.ts and zodv3.test.ts.snap
@MathurAditya724 MathurAditya724 force-pushed the fix/operationid-pascalcase branch from ede7565 to 341b6df Compare January 21, 2026 13:49
@MathurAditya724 MathurAditya724 merged commit 5f2db46 into rhinobase:main Jan 21, 2026
4 checks passed
@matzehecht matzehecht deleted the fix/operationid-pascalcase branch January 26, 2026 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants