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

Correct escaping of forward slash in pattern regex, deprioritizing escaping of unnecessarily escaped forward slash in pattern regex #288

Merged
merged 5 commits into from
Apr 17, 2024

Conversation

mjperrone
Copy link
Contributor

@mjperrone mjperrone commented Apr 16, 2024

Fixing the test definition

First I fix the issue with the test I initially added here.

The core issue was not acknowledging that the test strings are written in javascript which has its own escaping rules, so the string that was being provided and tested for was wrong. I'll put details in-line in the PR

Fixing the implementation

For the case that my PR broke (/ needs to end up as /\//), it's easy to understand what to do
I add back the .replace(/\//g, "\\/") to transform / to \/ (to prepare the pattern for being defined in code with a regex string (/<regex in here>/). That's easy to understand when you look at the test case description I put in the PR.

What was happening in my initial bug report?

In this bug report I indicated that the pattern .+\/.+ produced /.+\\/.+/ output code, which is invalid because the / in the middle of the regex is not escaped.

To simplify further the pattern \/ yields the code /\\//.

What does \/ in an initial pattern mean? It means the character /, written with an unnecessary escape character.

Should openapi-zod-client support transforming regexes that have unnecessary escape characters?

OpenAPI Spec points to JSON Schema spec, which points to ECMA-262, which is a little too dense for me to quickly get to a definitive answer.

In Javascript, it's totally fine to have an unnecessary escape character when defining regular expressions:

const re = new RegExp("\\/");
re.test("/")
true

const re = new RegExp("/");
re.test("/")
true

So I'm inclined to think that openapi-zod-client should support this.

how to support it?

To address the case where the user gives a pattern with an unnecessary escape character like /\, what is the right output code?

probably /\//

However that means the existing replace .replace(/\//g, "\\/"); ( / -> \/) would get in the way of this solution.

Another option could be to replace \/ with / first. That way \/ -> / -> \/ in the final output. That works, but would break the situation where the user specifies an escaped backslash followed by an unescaped forward slash like \\/. That would go \\/ -> \/ -> \\/ when we'd want to end up with \\\/.

I'm not sure a better way to address this situation. I'm publishing this PR as is because I think it's more important to address the case where \ is specified without an unnecessary escape character.

Copy link

vercel bot commented Apr 16, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
openapi-zod-client-rim4 ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 17, 2024 4:37pm

@@ -8,11 +8,11 @@ test("regex-with-escapes", () => {
properties: {
str: {
type: "string",
pattern: "^\/$"
pattern: "^/$"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

When this string is loaded into memory it will be: ^/$.

console.log("^/$")
^/$

The previous \ was unnecessary, an escape character for a character that doesn't need escaping, resulting in the character anyways:

console.log("^\/$")
^/$

},
}
}})
).toMatchInlineSnapshot(
'"z.object({ str: z.string().regex(/^\/$/) }).partial().passthrough()"'
'"z.object({ str: z.string().regex(/^\\/$/) }).partial().passthrough()"'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The point of this test is that we want the resulting code to include the escape character \ before the /, otherwise the / is interpreted as the end of the regex!

Since the \ wasn't escaped in the string definition, same as above it wasn't being included in the string in memory.

console.log("/^\/$/")
/^/$/
console.log("/^\\/$/")
/^\/$/

@astahmer
Copy link
Owner

nice ! can you add a changeset so I can merge it ?

@mjperrone mjperrone changed the title correct test expectation to look for a backslash Correct escaping of forward slash in pattern regex, deprioritizing escaping of unnecessarily escaped forward slash in pattern regex Apr 17, 2024
@mjperrone
Copy link
Contributor Author

nice ! can you add a changeset so I can merge it ?

I added a changeset and a test showing the remaining issue.

I marked the changeset patch but arguable this change (and the one in the previous PR) are breaking changes in that they change the output code without users changing their OpenAPI specs. I will leave you the decision of which semver bump to use.

@astahmer astahmer merged commit 3799ae3 into astahmer:main Apr 17, 2024
7 checks passed
@github-actions github-actions bot mentioned this pull request Apr 17, 2024
@mjperrone
Copy link
Contributor Author

Thanks! Looks like this still needs to be merged to get the release out: #289

@mjperrone mjperrone deleted the mp/fix-slash-escaping branch April 18, 2024 00:29
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