Skip to content

fix(convertUtilitiesToV4): drop js-regexp-lookbehind for improved browser support #1555

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/open-toys-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"flowbite-react": patch
---

refactor(ui~convertUtilitiesToV4): drop `js-regexp-lookbehind` in favor of more simpler regex for improved browser support
1 change: 0 additions & 1 deletion apps/web/content/docs/getting-started/ai-integration.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
title: AI and LLM Integration with Flowbite React
description: Learn how to integrate Flowbite React with AI models, LLMs, and chatbots using our specialized documentation routes and markdown accessibility features
keywords: AI integration, LLM integration, React AI, AI documentation, ChatGPT integration, AI components, language models, machine learning, documentation API, markdown API
---

Flowbite React provides powerful, built-in support for AI and Large Language Model (LLM) integration through specialized routes that expose documentation in machine-readable formats. These features enable seamless integration with ChatGPT, Claude, and other AI assistants.
Expand Down
46 changes: 32 additions & 14 deletions packages/ui/src/helpers/convert-utilities-to-v4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,29 @@ export function convertUtilitiesToV4(classNames: string): string {
return cacheValue;
}

let result = classNames;
const parts = classNames.split(/(\s+)/);
const result = parts
.map((part) => {
if (/^\s+$/.test(part)) {
return part;
}

for (const [regex, replacement] of regexMap) {
result = result.replace(regex, replacement);
}
const processed = part;
const modifierMatch = processed.match(/^([^:]+:)?(.+)$/);

if (modifierMatch) {
Comment on lines +29 to +31
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Potential issue with multiple colons in modifiers.
The regex ^([^:]+:)?(.+)$ captures only one optional colon-delimited prefix. This may fail to handle scenarios like hover:focus:bg-red-500. If supporting multiple modifiers is intended, consider updating the capture group to handle multiple colons.


🏁 Script executed:

#!/bin/bash
# Description: Search codebase for possible usage of multiple-colon class names 
# to confirm if multiple modifiers are used.

rg -A 3 "hover:.*:.*" 

Length of output: 72460


Attention: Update Regex to Support Compound Modifiers

The current regex in packages/ui/src/helpers/convert-utilities-to-v4.ts (lines 29–31):

- const modifierMatch = processed.match(/^([^:]+:)?(.+)$/);
+ const modifierMatch = processed.match(/^((?:[^:]+:)+)?(.+)$/);

only allows for one optional modifier prefix. However, our codebase and tests demonstrate compound modifier patterns (e.g. hover:focus:bg-red-500, dark:hover:!text-lg). This means that when multiple modifiers are used, only the first (e.g. "hover:") is captured, leaving the rest in the second group—which can lead to incorrect processing or conversion.

Recommendation:

  • Update the regex as shown above so that the optional group can match one or more colon-delimited prefixes.
  • Verify that the rest of the conversion logic correctly handles the split between modifiers and the utility.

Please address this to ensure consistency when multiple modifiers are present.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const modifierMatch = processed.match(/^([^:]+:)?(.+)$/);
if (modifierMatch) {
const modifierMatch = processed.match(/^((?:[^:]+:)+)?(.+)$/);
if (modifierMatch) {

const [, modifier = "", baseClass] = modifierMatch;

for (const [regex, replacement] of regexMap) {
if (regex.test(baseClass)) {
return modifier + baseClass.replace(regex, replacement);
}
}
}

return processed;
})
.join("");

cache.set(cacheKey, result);

Expand All @@ -44,15 +62,15 @@ export function convertUtilitiesToV4(classNames: string): string {
| ring | ring-3 |
*/
const regexMap = [
[/\b(shadow-sm)\b/g, "shadow-xs"],
[/(?<!-)(shadow)(?!-)\b/g, "shadow-sm"],
[/\b(drop-shadow-sm)\b/g, "drop-shadow-xs"],
[/\b(drop-shadow)\b(?!-)/g, "drop-shadow-sm"],
[/\b(blur-sm)\b/g, "blur-xs"],
[/\b(blur)\b(?!-)/g, "blur-sm"],
[/\b(rounded-sm)\b/g, "rounded-xs"],
[/\b(rounded)\b(?!-)/g, "rounded-sm"],
[/^shadow-sm$/, "shadow-xs"],
[/^shadow$/, "shadow-sm"],
[/^drop-shadow-sm$/, "drop-shadow-xs"],
[/^drop-shadow$/, "drop-shadow-sm"],
[/^blur-sm$/, "blur-xs"],
[/^blur$/, "blur-sm"],
[/^rounded-sm$/, "rounded-xs"],
[/^rounded$/, "rounded-sm"],
// TODO: revisit this - it breaks anything focused using tab
// [/\b(outline-none)\b/g, "outline-hidden"],
[/\b(ring)\b(?!-)/g, "ring-3"],
// [/^outline-none$/, "outline-hidden"],
[/^ring$/, "ring-3"],
] as const;
Loading