From 654681df9cc1026985c1888e04acb8931e91f1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcholas=20Andr=C3=A9?= Date: Thu, 14 Mar 2024 10:32:16 -0300 Subject: [PATCH] fix: replace camelcase with a version that works across node and browser (#723) --- .changeset/eight-cougars-love.md | 5 + packages/core/src/react/blocks/utils.ts | 4 +- .../core/src/utils/__tests__/camelcase.ts | 29 +++++ packages/core/src/utils/camelcase.ts | 122 +----------------- 4 files changed, 41 insertions(+), 119 deletions(-) create mode 100644 .changeset/eight-cougars-love.md create mode 100644 packages/core/src/utils/__tests__/camelcase.ts diff --git a/.changeset/eight-cougars-love.md b/.changeset/eight-cougars-love.md new file mode 100644 index 000000000..8a3ac0610 --- /dev/null +++ b/.changeset/eight-cougars-love.md @@ -0,0 +1,5 @@ +--- +"@headstartwp/core": patch +--- + +Fix: replace camelcase underlying implementation as the previous one only worked in node diff --git a/packages/core/src/react/blocks/utils.ts b/packages/core/src/react/blocks/utils.ts index 72393d2fe..d1321b0ac 100644 --- a/packages/core/src/react/blocks/utils.ts +++ b/packages/core/src/react/blocks/utils.ts @@ -1,5 +1,5 @@ import { Element } from 'html-react-parser'; -import camelCase from '../../utils/camelcase'; +import camelcase from '../../utils/camelcase'; import { Colors } from './types'; export function getAlignStyle(domNode: Element) { @@ -133,7 +133,7 @@ export function getInlineStyles(domNode: Element) { const position = style.indexOf(':'); const prop = style.substring(0, position).trim(); const value = style.substring(position).substring(1).trim(); - stylesObject[camelCase(prop, {})] = value; + stylesObject[camelcase(prop)] = value; }); return stylesObject; diff --git a/packages/core/src/utils/__tests__/camelcase.ts b/packages/core/src/utils/__tests__/camelcase.ts new file mode 100644 index 000000000..0c75449b8 --- /dev/null +++ b/packages/core/src/utils/__tests__/camelcase.ts @@ -0,0 +1,29 @@ +import camelcase from '../camelcase'; + +describe('camecase', () => { + it('works with general strings', () => { + expect(camelcase('foo-bar')).toBe('fooBar'); + expect(camelcase('foo-bar-baz')).toBe('fooBarBaz'); + expect(camelcase('foo--bar')).toBe('fooBar'); + expect(camelcase('--foo-bar')).toBe('fooBar'); + expect(camelcase('--foo--bar')).toBe('fooBar'); + expect(camelcase('FOO-BAR')).toBe('fooBar'); + expect(camelcase('FOÈ-BAR')).toBe('foèBar'); + expect(camelcase('-foo-bar-')).toBe('fooBar'); + expect(camelcase('--foo--bar--')).toBe('fooBar'); + expect(camelcase('foo.bar')).toBe('fooBar'); + expect(camelcase('foo..bar')).toBe('fooBar'); + expect(camelcase('..foo..bar..')).toBe('fooBar'); + expect(camelcase('foo_bar')).toBe('fooBar'); + expect(camelcase('__foo__bar__')).toBe('fooBar'); + expect(camelcase('foo bar')).toBe('fooBar'); + expect(camelcase(' foo bar ')).toBe('fooBar'); + expect(camelcase('-')).toBe(''); + expect(camelcase(' - ')).toBe(''); + expect(camelcase('margin-top')).toBe('marginTop'); + expect(camelcase('height')).toBe('height'); + expect(camelcase('padding-top')).toBe('paddingTop'); + expect(camelcase('border-block-end-color')).toBe('borderBlockEndColor'); + expect(camelcase('@font-feature-values')).toBe('@fontFeatureValues'); + }); +}); diff --git a/packages/core/src/utils/camelcase.ts b/packages/core/src/utils/camelcase.ts index 2fcb40b45..923a8e074 100644 --- a/packages/core/src/utils/camelcase.ts +++ b/packages/core/src/utils/camelcase.ts @@ -1,118 +1,6 @@ -// https://github.com/sindresorhus/camelcase/blob/main/index.js -/* eslint-disable no-param-reassign */ +function camelcase(text: string) { + const a = text.toLowerCase().replace(/[-_\s.]+(.)?/g, (_, c) => (c ? c.toUpperCase() : '')); + return a.substring(0, 1).toLowerCase() + a.substring(1); +} -const UPPERCASE = /[\p{Lu}]/u; -const LOWERCASE = /[\p{Ll}]/u; -const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu; -const IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u; -const SEPARATORS = /[_.\- ]+/; - -const LEADING_SEPARATORS = new RegExp(`^${SEPARATORS.source}`); -const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gu'); -const NUMBERS_AND_IDENTIFIER = new RegExp(`\\d+${IDENTIFIER.source}`, 'gu'); - -const preserveCamelCase = (string, toLowerCase, toUpperCase) => { - let isLastCharLower = false; - let isLastCharUpper = false; - let isLastLastCharUpper = false; - - for (let i = 0; i < string.length; i++) { - const character = string[i]; - - if (isLastCharLower && UPPERCASE.test(character)) { - string = `${string.slice(0, i)}-${string.slice(i)}`; - isLastCharLower = false; - isLastLastCharUpper = isLastCharUpper; - isLastCharUpper = true; - i++; - } else if (isLastCharUpper && isLastLastCharUpper && LOWERCASE.test(character)) { - string = `${string.slice(0, i - 1)}-${string.slice(i - 1)}`; - isLastLastCharUpper = isLastCharUpper; - isLastCharUpper = false; - isLastCharLower = true; - } else { - isLastCharLower = - toLowerCase(character) === character && toUpperCase(character) !== character; - isLastLastCharUpper = isLastCharUpper; - isLastCharUpper = - toUpperCase(character) === character && toLowerCase(character) !== character; - } - } - - return string; -}; - -const preserveConsecutiveUppercase = (input, toLowerCase) => { - LEADING_CAPITAL.lastIndex = 0; - - return input.replace(LEADING_CAPITAL, (m1) => toLowerCase(m1)); -}; - -const postProcess = (input, toUpperCase) => { - SEPARATORS_AND_IDENTIFIER.lastIndex = 0; - NUMBERS_AND_IDENTIFIER.lastIndex = 0; - - return input - .replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier)) - .replace(NUMBERS_AND_IDENTIFIER, (m) => toUpperCase(m)); -}; - -const camelCase = (input, options) => { - if (!(typeof input === 'string' || Array.isArray(input))) { - throw new TypeError('Expected the input to be `string | string[]`'); - } - - options = { - pascalCase: false, - preserveConsecutiveUppercase: false, - ...options, - }; - - if (Array.isArray(input)) { - input = input - .map((x) => x.trim()) - .filter((x) => x.length) - .join('-'); - } else { - input = input.trim(); - } - - if (input.length === 0) { - return ''; - } - - const toLowerCase = - options.locale === false - ? (string) => string.toLowerCase() - : (string) => string.toLocaleLowerCase(options.locale); - const toUpperCase = - options.locale === false - ? (string) => string.toUpperCase() - : (string) => string.toLocaleUpperCase(options.locale); - - if (input.length === 1) { - return options.pascalCase ? toUpperCase(input) : toLowerCase(input); - } - - const hasUpperCase = input !== toLowerCase(input); - - if (hasUpperCase) { - input = preserveCamelCase(input, toLowerCase, toUpperCase); - } - - input = input.replace(LEADING_SEPARATORS, ''); - - if (options.preserveConsecutiveUppercase) { - input = preserveConsecutiveUppercase(input, toLowerCase); - } else { - input = toLowerCase(input); - } - - if (options.pascalCase) { - input = toUpperCase(input.charAt(0)) + input.slice(1); - } - - return postProcess(input, toUpperCase); -}; - -export default camelCase; +export default camelcase;