mesqueeb/case-anything

Question: Prevent @ $ characters from being removed

thim81 opened this issue ยท 18 comments

Hi @mesqueeb,

Thanks for the creation of such a powerful, yet easy to use package + the footprint is nice & small.

I noticed that when transforming a string with an @ or $ character, the @ & $ gets removed.
Example:

  • camelCase("@nextlink") becomes ==> "nextLink"
  • camelCase("$filter") becomes ==> "filter"

Is there a way to prevent this?

@thim81 this was quite an interesting issue to solve.

I finished an entire rewrite of the library on a new branch. There are breaking changes.

I'm interested to get your feedback. Please review lines 251 ~ 300 from this file:

https://github.com/mesqueeb/case-anything/blob/special-character-playground/test/index.ts/#L251-L304

each line, you can see the input string and the expected result.

Does this align with your use case?
Do you have some example strings for me so I can run them through the new system?

PS: The rewrite took me 4 hours ๐Ÿ˜…

--
case-anything was made with โ™ฅ by Luca Ban.
If you use this library in your projects, you can support the maintenance of this library by a small contribution via Github ๐Ÿ’œ.

Hi @mesqueeb

I had a look at the test cases & the results which do make sense. I see you even include support for the "." character.

My main use-case is to transform query/header/path field names and request properties, which can contain some special characters.
Query parameters example https://example.com/path/to/$orderby?=ferret&$top=10 where $orderby and $top are the field names, that will be cased with "case-anything".

Example request body properties:

{
  "value": [],
  "@count": 68,
  "@nextLink": "https://customername.smc.slgnt.eu/lists/v2/audiences?$count=true&$top=3&$skip=3"
}

some context: I'm implementing your excellent "case-anything" in a small OpenAPI formatting package that I have built.

This item started as a question, so if you feel that the @$ removal behaviour don't belong in your package, I would understand that and I try to figure out a way for it when implementing your package.

It was never my intention to use up 4 hours of your time ๐Ÿคญ, but I'm very grateful and hope that your branch becomes part of the release ๐Ÿคž .

That's ok! I enjoyed the challenge.

I added some more tests this morning to represent a potential query.

Please look at lines 376 ~ 400:
https://github.com/mesqueeb/case-anything/blob/9dbb3c32a33180b4226acd5ec3ebca7c0a41be22/test/index.ts/#L376-L400

I don't feel like any of these would be particularly useful for you though? : S
which usage are you looking for exactly? I'd feel weird releasing something that's kinda besides the point.

Can you give me more detailed use-case examples. Ideally you give me the desired input and output.

@mesqueeb

I had a look the additional test, there are some strange results.

const query = `$orderBy=name&skip=3&sort=true&name=James-P&email=sullivan@monsters.inc`
test('kebabCase(query) - dont strip', (t) => t.is(kebabCase(query, false), '$order-by-=name-&skip-=3-&sort-=true-&name-=james--p-&email-=sullivan-@monsters-.inc')) // prettier-ignore

I would expect $order-by=name-&skip=3-&sort=true&name-=james--p&email=sullivan@monsters.inc
without the additional "-" behind $order-by- and before the @ & . sullivan-@monsters-.inc

Which seems to happen for the whole range of tests after the pascalCase tests.

The use-case would be really transforming property names, that are defined in a OpenAPI YAML file.

Like so:

components:
  parameters:
    odataCount:
      name: $count
      in: query
      description: Request a count of matching items included with the returned results
      schema:
        type: boolean
        default: false
    odataFilter:
      name: $filterItems
      in: query
      required: false
      description: An expression used to filter the returned items
      schema:
        type: string
        example: name+eq+'contact'
        default: name+eq+'contact'
  schemas:
    MessageMailResults:
      description: A filtered list of sent email messages.
      type: object
      properties:
        value:
          description: "List of sent email messages, based on the query options."
          type: array
          items:
            $ref: "#/components/schemas/MessageMailResultData"
        "@nextLink":
          $ref: "#/components/schemas/LinkNext"

where I'm using the casing formatting to modify, as example:
"@nextLink" to a kebab-case: "@-next-link"
and name: $filterItems to PascalCase name: $FilterItems

for

const query = `$orderBy=name&skip=3&sort=true&name=James-P&email=sullivan@monsters.inc`

current implementation on the new branch:

const stripSpecial = false
kebabCase(query, stripSpecial)
// becomes
'$order-by-=name-&skip-=3-&sort-=true-&name-=james--p-&email-=sullivan-@monsters-.inc'

steps:

  1. split on words
  2. attach special characters back in front of words
  3. join with -

But I think it indeed makes little sense.
I can try to change it to:

  1. split on words
  2. join with - OR with special character if there was a special character instead.

that would result in this:

const stripSpecial = false
kebabCase(query, stripSpecial)
// becomes
'$order-by=name&skip=3&sort=true&name=james-p&email=sullivan@monsters.inc'

makes muuuch more sense.

Does this suit your use case? I think I'll go with this version if you like it.

@thim81 I have finished my proposed implementation and am much more satisfied with the results.

I have made the tests more readable as well, and easier for you to find your exact use cases you want to review.

This time, please review from lines 350 ~ until the end of the file:

https://github.com/mesqueeb/case-anything/blob/e43fcac06b178567ab589ea03f006ea9e090a8b4/test/index.ts#L350-L478

Please note that as soon as there is one space in your query, it will throw things off quite a bit. This is the nature of case-anything I will not change. Because of my philosophy:

As soon as there is a space in the target string, it will regard the input as a sentence and only split each part at the spaces.

So in your case I would find and replace any spaces with a temporary symbol on beforehand, then apply kebabCase and then put the spaces back.

Eg. something like:

kebabCase(query.replaceAll(' ', '|||')).replaceAll('|||', ' ')

@mesqueeb I had a quick look at the lines 350 ~ and noticed some unwanted side-effect:

Example: line 420 t.is(pascalCase($orderBy=name&...where$orderBybecomes$orderbywhile with pascalCase I would have expected$OrderBy`

@thim81 your comment is a bit hard to read. Can you add three ``` above and below the example you wanna show me?
๐Ÿ˜…

BTW, I think you read the test wrong. The input is $orderBy the output is the line underneath that:

image

@thim81 did you see this ?

@mesqueeb I only saw it this morning, so apologies for the late response.

I took a screenshot with the expected result:
2021-12-30 at 09 58 01@2x

@thim81 that is because there is a space in that query! ๐Ÿ˜„

Please note that as soon as there is one space in your query, it will throw things off quite a bit. This is the nature of case-anything I will not change. Because of my philosophy:

As soon as there is a space in the target string, it will regard the input as a sentence and only split each part at the spaces.

So in your case I would find and replace any spaces with a temporary symbol on beforehand, then apply kebabCase and then put the spaces back.

Eg. something like:

kebabCase(query.replaceAll(' ', '|||')).replaceAll('|||', ' ')

A little higher in the tests I have the example of the same query without spaces. I think that's your desired behaviour.

๐Ÿคฆ how could I have missed that, apologies (again).

FYI: a query string with spaces is not valid, since the browser would not properly handle that, so users would use the "+" character OR "%20" https://stackoverflow.com/questions/1634271/url-encoding-the-space-character-or-20

But your workaround with the replace is very handy to keep in mind.

I have prepared a range of feature PR's for new casing options, so looking forward for your remarks and comments.

I have ran the tests locally and they all pass. Perhaps it might be handy for you to setup a "test" action using the Github actions, that way you are certain the PR's pass the test suite.

@thim81 thank you so much! So looking at the query example without spaces, this is in line with your desired behaviour ? If so I'll make sure to release this asap.

Also thanks for the other PRs! I'll merge and release them as well : )

The example without the spaces is exactly the expected behaviour ๐Ÿ™Œ

@thim81
all changes are released as v2

See change notes:
https://github.com/mesqueeb/case-anything/releases/tag/v2.0.0

and also check the brand new readme:
https://github.com/mesqueeb/case-anything#readme

Nice ๐Ÿ™Œ

The readme looks great, very clear.

Thanks for the time and effort you have put into the package, it is greatly appreciated.