flowbite-react
Version:
Official React components built for Flowbite and Tailwind CSS
194 lines (191 loc) • 7.17 kB
JavaScript
import MagicString from 'magic-string';
import { parseSync, Visitor } from 'oxc-parser';
const COMPOUND_TO_SIMPLE_MAP = {
// Accordion
"Accordion.Panel": "AccordionPanel",
"Accordion.Title": "AccordionTitle",
"Accordion.Content": "AccordionContent",
// Avatar
"Avatar.Group": "AvatarGroup",
"Avatar.Counter": "AvatarGroupCounter",
// Banner
"Banner.CollapseButton": "BannerCollapseButton",
// Breadcrumb
"Breadcrumb.Item": "BreadcrumbItem",
// Button
"Button.Group": "ButtonGroup",
// Clipboard
"Clipboard.WithIcon": "ClipboardWithIcon",
"Clipboard.WithIconText": "ClipboardWithIconText",
// Drawer
"Drawer.Header": "DrawerHeader",
"Drawer.Items": "DrawerItems",
// Dropdown
"Dropdown.Item": "DropdownItem",
"Dropdown.Header": "DropdownHeader",
"Dropdown.Divider": "DropdownDivider",
// Footer
"Footer.Brand": "FooterBrand",
"Footer.Copyright": "FooterCopyright",
"Footer.Divider": "FooterDivider",
"Footer.Icon": "FooterIcon",
"Footer.Link": "FooterLink",
"Footer.LinkGroup": "FooterLinkGroup",
"Footer.Title": "FooterTitle",
// HR
"HR.Icon": "HRIcon",
"HR.Square": "HRSquare",
"HR.Text": "HRText",
"HR.Trimmed": "HRTrimmed",
// List
"List.Item": "ListItem",
"ListGroup.Item": "ListGroupItem",
// MegaMenu
"MegaMenu.Dropdown": "MegaMenuDropdown",
"MegaMenu.DropdownToggle": "MegaMenuDropdownToggle",
// Modal
"Modal.Header": "ModalHeader",
"Modal.Body": "ModalBody",
"Modal.Footer": "ModalFooter",
// Navbar
"Navbar.Brand": "NavbarBrand",
"Navbar.Collapse": "NavbarCollapse",
"Navbar.Link": "NavbarLink",
"Navbar.Toggle": "NavbarToggle",
// Pagination
"Pagination.Button": "PaginationButton",
// Rating
"Rating.Star": "RatingStar",
"Rating.Advanced": "RatingAdvanced",
// Sidebar
"Sidebar.CTA": "SidebarCTA",
"Sidebar.Collapse": "SidebarCollapse",
"Sidebar.Item": "SidebarItem",
"Sidebar.Items": "SidebarItems",
"Sidebar.ItemGroup": "SidebarItemGroup",
"Sidebar.Logo": "SidebarLogo",
// Table
"Table.Head": "TableHead",
"Table.Body": "TableBody",
"Table.Row": "TableRow",
"Table.Cell": "TableCell",
"Table.HeadCell": "TableHeadCell",
// Tabs
"Tabs.Item": "TabItem",
// Timeline
"Timeline.Item": "TimelineItem",
"Timeline.Point": "TimelinePoint",
"Timeline.Content": "TimelineContent",
"Timeline.Time": "TimelineTime",
"Timeline.Title": "TimelineTitle",
"Timeline.Body": "TimelineBody",
// Toast
"Toast.Toggle": "ToastToggle"
};
function transform(content) {
try {
const result = parseSync("file.tsx", content);
if (result.errors.length > 0) {
console.warn("Warning: Parsing errors detected, skipping transformation");
return { content, changed: false };
}
const flowbiteImportSpecifiers = [];
const flowbiteImportInfos = [];
const replacements = [];
const newImportsToAdd = /* @__PURE__ */ new Set();
const importVisitor = new Visitor({
ImportDeclaration(node) {
if (node.source?.value === "flowbite-react" || typeof node.source?.value === "string" && node.source.value.startsWith("flowbite-react/components/")) {
if (Array.isArray(node.specifiers) && node.specifiers.every((s2) => s2.type === "ImportSpecifier")) {
const importText = content.slice(node.start, node.end);
const braceStart = importText.indexOf("{");
const braceEnd = importText.lastIndexOf("}");
if (braceStart !== -1 && braceEnd !== -1) {
const importInfo = {
start: node.start,
end: node.end,
specifiersStart: node.start + braceStart + 1,
specifiersEnd: node.start + braceEnd,
specifiers: [],
source: node.source?.value
};
node.specifiers.forEach((specifier) => {
if (specifier.imported?.type === "Identifier") {
flowbiteImportSpecifiers.push(specifier.imported.name);
importInfo.specifiers.push({
name: specifier.imported.name,
originalText: content.slice(specifier.start, specifier.end)
});
}
});
flowbiteImportInfos.push(importInfo);
}
}
}
}
});
importVisitor.visit(result.program);
const jsxVisitor = new Visitor({
JSXMemberExpression(node) {
if (node.object?.type === "JSXIdentifier" && node.property?.type === "JSXIdentifier" && flowbiteImportSpecifiers.includes(node.object.name)) {
const compoundName = `${node.object.name}.${node.property.name}`;
const simpleName = COMPOUND_TO_SIMPLE_MAP[compoundName];
if (simpleName && flowbiteImportInfos.length > 0) {
replacements.push({
start: node.start,
end: node.end,
newText: simpleName
});
if (!flowbiteImportSpecifiers.includes(simpleName) && !newImportsToAdd.has(simpleName)) {
newImportsToAdd.add(simpleName);
}
}
}
}
});
jsxVisitor.visit(result.program);
if (replacements.length === 0) {
return { content, changed: false };
}
const finalImportInfo = flowbiteImportInfos.find((info) => info.source === "flowbite-react") || flowbiteImportInfos[0] || null;
const s = new MagicString(content);
const sortedReplacements = [...replacements].sort((a, b) => b.start - a.start);
for (const { start, end, newText } of sortedReplacements) {
s.overwrite(start, end, newText);
}
if (newImportsToAdd.size > 0 && finalImportInfo) {
const existingSpecifierTexts = finalImportInfo.specifiers.map((s2) => s2.originalText);
const existingNames = new Set(finalImportInfo.specifiers.map((s2) => s2.name));
const newSpecifierNames = [...newImportsToAdd].filter((name) => !existingNames.has(name));
const allSpecifiers = [
...existingSpecifierTexts.map((text, i) => ({ text, name: finalImportInfo.specifiers[i].name })),
...newSpecifierNames.map((name) => ({ text: name, name }))
].sort((a, b) => a.name.localeCompare(b.name));
const originalImportText = content.slice(finalImportInfo.specifiersStart, finalImportInfo.specifiersEnd);
const isMultiline = originalImportText.includes("\n");
let newSpecifiersText;
if (isMultiline) {
newSpecifiersText = "\n " + allSpecifiers.map((s2) => s2.text).join(",\n ") + ",\n";
} else {
newSpecifiersText = " " + allSpecifiers.map((s2) => s2.text).join(", ") + " ";
}
s.overwrite(finalImportInfo.specifiersStart, finalImportInfo.specifiersEnd, newSpecifiersText);
}
return {
content: s.toString(),
changed: true
};
} catch (_error) {
console.warn("Warning: Could not parse file, skipping transformation");
return {
content,
changed: false
};
}
}
const compoundComponentsTransformer = {
name: "Compound Components",
transform
};
export { compoundComponentsTransformer };
//# sourceMappingURL=compound-components.js.map