-
-
Notifications
You must be signed in to change notification settings - Fork 369
feat: support for more String transformation actions #1558
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| export * from './toCamelCase.ts'; | ||
| export * from './toCamelCase.ts'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,36 +1,33 @@ | ||
| import type { BaseTransformation } from '../../types/index.ts'; | ||
| import { _formatCase } from '../../utils/index.ts'; | ||
|
|
||
| /** Splits a string into words for case conversion. */ | ||
| function splitWords(value: string): string[] { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Reimplementing case-splitting logic in a local splitWords() helper instead of reusing the shared Prompt for AI agents |
||
| // Split on non-alphanumeric boundaries and existing case/separator breaks. | ||
| return value | ||
| .replace(/([a-z0-9])([A-Z])/g, '$1 $2') | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Prompt for AI agents |
||
| .replace(/[_\-\s]+/g, ' ') | ||
| .split(' ') | ||
| .map((part) => part.trim()) | ||
| .filter((part) => part.length > 0); | ||
| } | ||
|
Comment on lines
+4
to
+12
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🌐 Web query:
💡 Result: Yes, Valibot's string case transformation actions (toCamelCase, toSnakeCase, toKebabCase, and toPascalCase) do split words based on acronym boundaries [1][2][3][4]. According to the official documentation, these actions treat acronym runs as distinct segments that are normalized to lowercase [2][3][4]. For example, the input parseURLValue is processed as parse, URL, and Value, resulting in outputs like [3][4]: - toCamelCase: parseUrlValue [3] - toSnakeCase: parse_url_value [4] - toKebabCase: parse-url-value [2] - toPascalCase: ParseUrlValue [5] The implementation uses a shared tokenizer utility that identifies word boundaries by underscores (_), hyphens (-), ASCII whitespace, and both case and acronym changes [1][2][3][6]. Digits remain attached to the preceding token (e.g., item2Name becomes item2-name) [2][3][4]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
git ls-files 'library/src/actions/toCamelCase/*' 'library/src/actions/*Case*' 'library/src/**/to*Case*' | sed -n '1,120p'Repository: open-circle/valibot Length of output: 1328 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Show the implementation and tests with line numbers.
sed -n '1,220p' library/src/actions/toCamelCase/toCamelCase.ts
printf '\n--- TESTS ---\n'
sed -n '1,240p' library/src/actions/toCamelCase/toCamelCase.test.tsRepository: open-circle/valibot Length of output: 6673 🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
import re
def split_words(value: str):
return [
part.strip()
for part in re.sub(r'([a-z0-9])([A-Z])', r'\1 \2', value)
.replace('_', ' ')
.replace('-', ' ')
.split(' ')
if part.strip()
]
def camel(value: str):
words = split_words(value)
return ''.join(
w.lower() if i == 0 else w[:1].upper() + w[1:].lower()
for i, w in enumerate(words)
)
for s in ["parseURLValue", "helloWorld", "item2Name", "HELLO_WORLD", "--foo__bar--", "foo\tbar\nbaz"]:
print(s, "=>", split_words(s), "=>", camel(s))
PYRepository: open-circle/valibot Length of output: 441 Split acronym boundaries in 🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
| * To camel case action interface. | ||
| * | ||
| * @beta | ||
| */ | ||
| export interface ToCamelCaseAction | ||
| extends BaseTransformation<string, string, never> { | ||
| /** | ||
| * The action type. | ||
| */ | ||
| readonly type: 'to_camel_case'; | ||
| /** | ||
| * The action reference. | ||
| */ | ||
| readonly reference: typeof toCamelCase; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a to camel case transformation action. | ||
| * | ||
| * Words are separated by `_`, `-` and ASCII whitespace, as well as by case | ||
| * and acronym boundaries. | ||
| * | ||
| * Hint: Acronym runs are normalized to lowercase (e.g. `parseURLValue` → | ||
| * `parseUrlValue`) and digits stay attached to the preceding token (e.g. | ||
| * `item2Name` → `item2Name`). | ||
| * Converts the input to camelCase, e.g. `"hello_world"` → `"helloWorld"`, | ||
| * `"foo-bar"` → `"fooBar"`. Existing case boundaries (e.g. `fooBar`) are | ||
| * preserved and re-joined. See issue #1324. | ||
| * | ||
| * @returns A to camel case action. | ||
| * | ||
| * @beta | ||
| */ | ||
| // @__NO_SIDE_EFFECTS__ | ||
| export function toCamelCase(): ToCamelCaseAction { | ||
|
|
@@ -40,8 +37,113 @@ export function toCamelCase(): ToCamelCaseAction { | |
| reference: toCamelCase, | ||
| async: false, | ||
| '~run'(dataset) { | ||
| dataset.value = _formatCase(dataset.value, '', false, true); | ||
| const words = splitWords(dataset.value); | ||
| dataset.value = words | ||
| .map((word, i) => | ||
| i === 0 | ||
| ? word.toLowerCase() | ||
| : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(), | ||
| ) | ||
| .join(''); | ||
| return dataset; | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * To snake case action interface. | ||
| */ | ||
| export interface ToSnakeCaseAction | ||
| extends BaseTransformation<string, string, never> { | ||
| readonly type: 'to_snake_case'; | ||
| readonly reference: typeof toSnakeCase; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a to snake case transformation action. | ||
| * | ||
| * Converts the input to snake_case, e.g. `"helloWorld"` → `"hello_world"`, | ||
| * `"foo-bar"` → `"foo_bar"`. See issue #1324. | ||
| * | ||
| * @returns A to snake case action. | ||
| */ | ||
| // @__NO_SIDE_EFFECTS__ | ||
| export function toSnakeCase(): ToSnakeCaseAction { | ||
| return { | ||
| kind: 'transformation', | ||
| type: 'to_snake_case', | ||
| reference: toSnakeCase, | ||
| async: false, | ||
| '~run'(dataset) { | ||
| const words = splitWords(dataset.value); | ||
| dataset.value = words.map((w) => w.toLowerCase()).join('_'); | ||
| return dataset; | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * To kebab case action interface. | ||
| */ | ||
| export interface ToKebabCaseAction | ||
| extends BaseTransformation<string, string, never> { | ||
| readonly type: 'to_kebab_case'; | ||
| readonly reference: typeof toKebabCase; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a to kebab case transformation action. | ||
| * | ||
| * Converts the input to kebab-case, e.g. `"helloWorld"` → `"hello-world"`, | ||
| * `"foo_bar"` → `"foo-bar"`. See issue #1324. | ||
| * | ||
| * @returns A to kebab case action. | ||
| */ | ||
| // @__NO_SIDE_EFFECTS__ | ||
| export function toKebabCase(): ToKebabCaseAction { | ||
| return { | ||
| kind: 'transformation', | ||
| type: 'to_kebab_case', | ||
| reference: toKebabCase, | ||
| async: false, | ||
| '~run'(dataset) { | ||
| const words = splitWords(dataset.value); | ||
| dataset.value = words.map((w) => w.toLowerCase()).join('-'); | ||
| return dataset; | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * To pascal case action interface. | ||
| */ | ||
| export interface ToPascalCaseAction | ||
| extends BaseTransformation<string, string, never> { | ||
| readonly type: 'to_pascal_case'; | ||
| readonly reference: typeof toPascalCase; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a to pascal case transformation action. | ||
| * | ||
| * Converts the input to PascalCase, e.g. `"hello_world"` → `"HelloWorld"`, | ||
| * `"foo-bar"` → `"FooBar"`. See issue #1324. | ||
| * | ||
| * @returns A to pascal case action. | ||
| */ | ||
| // @__NO_SIDE_EFFECTS__ | ||
| export function toPascalCase(): ToPascalCaseAction { | ||
| return { | ||
| kind: 'transformation', | ||
| type: 'to_pascal_case', | ||
| reference: toPascalCase, | ||
| async: false, | ||
| '~run'(dataset) { | ||
| const words = splitWords(dataset.value); | ||
| dataset.value = words | ||
| .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()) | ||
| .join(''); | ||
| return dataset; | ||
| }, | ||
| }; | ||
| } | ||
|
Comment on lines
+53
to
+149
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check whether dedicated action files for toSnakeCase/toKebabCase/toPascalCase already exist
fd -a 'toSnakeCase.ts|toKebabCase.ts|toPascalCase.ts' library/src/actions
echo "---"
cat -n library/src/actions/toSnakeCase/toSnakeCase.ts 2>/dev/null
echo "---"
cat -n library/src/actions/toKebabCase/toKebabCase.ts 2>/dev/null
echo "---"
cat -n library/src/actions/toPascalCase/toPascalCase.ts 2>/dev/nullRepository: open-circle/valibot Length of output: 4741 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '--- toCamelCase.ts ---\n'
cat -n library/src/actions/toCamelCase/toCamelCase.ts
printf '\n--- toCamelCase/index.ts ---\n'
cat -n library/src/actions/toCamelCase/index.ts
printf '\n--- actions/index.ts ---\n'
cat -n library/src/actions/index.ts
printf '\n--- exports referencing toSnakeCase/toKebabCase/toPascalCase ---\n'
rg -n "toSnakeCase|toKebabCase|toPascalCase" library/src/actions -g '!**/*.d.ts'Repository: open-circle/valibot Length of output: 248 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '--- library/src/actions/toCamelCase/index.ts ---\n'
cat -n library/src/actions/toCamelCase/index.ts
printf '\n--- library/src/actions/index.ts ---\n'
cat -n library/src/actions/index.ts
printf '\n--- library/src/actions/toSnakeCase/index.ts ---\n'
cat -n library/src/actions/toSnakeCase/index.ts
printf '\n--- library/src/actions/toKebabCase/index.ts ---\n'
cat -n library/src/actions/toKebabCase/index.ts
printf '\n--- library/src/actions/toPascalCase/index.ts ---\n'
cat -n library/src/actions/toPascalCase/index.tsRepository: open-circle/valibot Length of output: 248 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- library/src/actions/toCamelCase/toCamelCase.ts ---'
cat -n library/src/actions/toCamelCase/toCamelCase.ts
echo
echo '--- library/src/actions/toCamelCase/index.ts ---'
cat -n library/src/actions/toCamelCase/index.ts
echo
echo '--- library/src/actions/index.ts ---'
cat -n library/src/actions/index.ts
echo
echo '--- library/src/actions/toSnakeCase/index.ts ---'
cat -n library/src/actions/toSnakeCase/index.ts
echo
echo '--- library/src/actions/toKebabCase/index.ts ---'
cat -n library/src/actions/toKebabCase/index.ts
echo
echo '--- library/src/actions/toPascalCase/index.ts ---'
cat -n library/src/actions/toPascalCase/index.tsRepository: open-circle/valibot Length of output: 11039 Remove the duplicate case actions from
🤖 Prompt for AI AgentsSource: Coding guidelines |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Duplicate re-export of
toCamelCase/index.ts.Line 104 already exports
./toCamelCase/index.ts; line 108 duplicates it verbatim. The line-range summary claims this was "a previously missing re-export," but the annotated file shows the export already existed — this is a redundant duplicate, not a new addition, and likely a leftover from the file-organization mixup intoCamelCase.ts(see companion comment there).🧹 Proposed fix
export * from './toDate/index.ts'; export * from './toKebabCase/index.ts'; export * from './toLowerCase/index.ts'; -export * from './toCamelCase/index.ts'; export * from './toMaxValue/index.ts';📝 Committable suggestion
🤖 Prompt for AI Agents