fix(dev): avoid polynomial regex backtracking in toCamelCase helper (#5148)

* fix(dev): avoid polynomial regex backtracking in toCamelCase helper

* Add test for toCamelCase with non-word character separators

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Eric Fennis
2026-11-02 20:18:25 +01:00
committed by GitHub
co-authored by Copilot Autofix powered by AI
parent b196797ede
commit 58c85b4758
3 changed files with 47 additions and 2 deletions
+3
View File
@@ -5,6 +5,9 @@
"description": "A internal used package with helpers.",
"main": "helpers.ts",
"types": "helpers.ts",
"scripts": {
"test": "vitest run"
},
"author": "",
"license": "ISC",
"type": "module",
@@ -0,0 +1,42 @@
import { describe, expect, it } from 'vitest';
import { toCamelCase } from './toCamelCase.ts';
describe('toCamelCase', () => {
it('converts kebab-case icon names', () => {
expect(toCamelCase('arrow-up')).toBe('arrowUp');
expect(toCamelCase('circle-arrow-out-down-left')).toBe('circleArrowOutDownLeft');
expect(toCamelCase('arrow-up-0-1')).toBe('arrowUp01');
});
it('treats whitespace, hyphens and underscores as separators', () => {
expect(toCamelCase('foo_bar')).toBe('fooBar');
expect(toCamelCase('foo bar')).toBe('fooBar');
expect(toCamelCase('foo\tbar')).toBe('fooBar');
expect(toCamelCase('foo - _\tbar')).toBe('fooBar');
expect(toCamelCase('-foo')).toBe('Foo');
});
it('lowercases only a leading capital letter', () => {
expect(toCamelCase('Foo-bar')).toBe('fooBar');
expect(toCamelCase('FooBar')).toBe('fooBar');
expect(toCamelCase('alreadyCamelCase')).toBe('alreadyCamelCase');
});
it('keeps separators that are not followed by a word character', () => {
expect(toCamelCase('foo-')).toBe('foo-');
expect(toCamelCase('foo__')).toBe('foo__');
expect(toCamelCase('foo\t!')).toBe('foo\t!');
expect(toCamelCase('')).toBe('');
});
it('handles long runs of tabs in linear time', () => {
const input = `a${'\t'.repeat(100_000)}!`;
const start = performance.now();
const result = toCamelCase(input);
const duration = performance.now() - start;
expect(result).toBe(input);
expect(duration).toBeLessThan(100);
});
});
+2 -2
View File
@@ -5,6 +5,6 @@
* @returns {string} A camelized string
*/
export const toCamelCase = (string: string): string =>
string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) =>
p2 ? p2.toUpperCase() : p1.toLowerCase(),
string.replace(/^([A-Z])|[\s-_]+(\w)?/g, (match, p1, p2) =>
p1 ? p1.toLowerCase() : p2 ? p2.toUpperCase() : match,
);