Files
HiveGrid-Wireframes/hivegrid-cloud/scripts/migrate-icons-to-feather.mjs
T

288 lines
12 KiB
JavaScript

// One-shot migration: rewrite every hand-authored SVG icon under
// apps/web/app/components/ui/icons/** to re-export a Subframe `@subframe/core`
// Feather icon via the `featherIcon` wrapper (which preserves the
// `({ className }: { className?: string })` public API so every call site keeps
// working unchanged).
//
// Run from repo root: node scripts/migrate-icons-to-feather.mjs
import { readFileSync, writeFileSync, existsSync } from "node:fs"
import { join, dirname } from "node:path"
import { fileURLToPath } from "node:url"
const __dirname = dirname(fileURLToPath(import.meta.url))
const repoRoot = join(__dirname, "..")
const iconsRoot = join(repoRoot, "apps/web/app/components/ui/icons")
// Validated list of every Feather export in @subframe/core@1.155.0.
const featherNames = new Set(
readFileSync("/tmp/feather-names.txt", "utf8").split("\n").map((s) => s.trim()).filter(Boolean)
)
// relative path (under icons/) -> Feather component name.
// Every target verified to exist in featherNames before any file is written.
const MAP = {
// --- top level ---
"ArrowsPointingIn.tsx": "FeatherShrink",
"ArrowsPointingOut.tsx": "FeatherExpand",
"AtSymbolIcon.tsx": "FeatherAtSign",
"BoardIcon.tsx": "FeatherTrello",
"BoardIconFilled.tsx": "FeatherTrello",
"BookmarkIcon.tsx": "FeatherBookmark",
"BookmarkIconFilled.tsx": "FeatherBookmark",
"BrilliantIdeaIcon.tsx": "FeatherLightbulb",
"BrilliantIdeaIconFilled.tsx": "FeatherLightbulb",
"CacheIcon.tsx": "FeatherDatabase",
"CalendarFilledIcon.tsx": "FeatherCalendar",
"CalendarIcon.tsx": "FeatherCalendar",
"ChatAltIcon.tsx": "FeatherMessageSquare",
"CheckEmptyCircleIcon.tsx": "FeatherCircle",
"CheckFilledCircleIcon.tsx": "FeatherCheckCircle",
"CheckIcon.tsx": "FeatherCheck",
"ChevronDownIcon.tsx": "FeatherChevronDown",
"ChevronLeftIcon.tsx": "FeatherChevronLeft",
"ClipboardIcon.tsx": "FeatherClipboard",
"ClockIcon.tsx": "FeatherClock",
"ClockIconFilled.tsx": "FeatherClock",
"ComboChartIcon.tsx": "FeatherBarChart2",
"CookieIcon.tsx": "FeatherCookie",
"CreditsIcon.tsx": "FeatherCoins",
"CreditsIconFilled.tsx": "FeatherCoins",
"CurrencyIcon.tsx": "FeatherDollarSign",
"DataSourceIcon.tsx": "FeatherDatabase",
"DataSourceIconFilled.tsx": "FeatherDatabase",
"DataViewIcon.tsx": "FeatherTable",
"DataViewIconFilled.tsx": "FeatherTable",
"DocumentDuplicateIcon.tsx": "FeatherCopy",
"DocumentDuplicateIconFilled.tsx": "FeatherCopy",
"DotsHorizontalEmptyIcon.tsx": "FeatherMoreHorizontal",
"DotsHorizontalFilledIcon.tsx": "FeatherMoreHorizontal",
"DotsVerticalEmptyIcon.tsx": "FeatherMoreVertical",
"DotsVerticalFilledIcon.tsx": "FeatherMoreVertical",
"DownArrow.tsx": "FeatherArrowDown",
"DownloadIcon.tsx": "FeatherDownload",
"EmailIcon.tsx": "FeatherMail",
"ExclamationTriangleIcon.tsx": "FeatherAlertTriangle",
"ExclamationTriangleIconFilled.tsx": "FeatherAlertTriangle",
"ExternalLinkEmptyIcon.tsx": "FeatherExternalLink",
"ExternalLinkFilledIcon.tsx": "FeatherExternalLink",
"EyeIcon.tsx": "FeatherEye",
"EyeIconFilled.tsx": "FeatherEye",
"EyeLashIcon.tsx": "FeatherEyeOff",
"EyeLashIconFilled.tsx": "FeatherEyeOff",
"FeedbackIcon.tsx": "FeatherMessageSquare",
"FeedbackIconFilled.tsx": "FeatherMessageSquare",
"FilterEmptyIcon.tsx": "FeatherFilter",
"FilterFilledIcon.tsx": "FeatherFilter",
"GearIcon.tsx": "FeatherSettings",
"GearIconFilled.tsx": "FeatherSettings",
"GitHubIcon.tsx": "FeatherGithub",
"GoogleIcon.tsx": "FeatherChrome",
"IconEmptyResults.tsx": "FeatherInbox",
"LanguageIcon.tsx": "FeatherLanguages",
"LightningBoltEmptyIcon.tsx": "FeatherZap",
"LightningBoltFilledIcon.tsx": "FeatherZap",
"LockClosedIcon.tsx": "FeatherLock",
"LockOpenedIcon.tsx": "FeatherUnlock",
"MagnifyingGlassIcon.tsx": "FeatherSearch",
"MegaphoneFilled.tsx": "FeatherMegaphone",
"MembershipCardIcon.tsx": "FeatherCreditCard",
"NewFieldIcon.tsx": "FeatherPlusSquare",
// NOTE: OpenCloseArrowIcon.tsx is intentionally NOT listed here. It is a
// stateful toggle button ({ open, setOpen }), not a bare `{ className }` icon,
// so it is migrated by hand (renders Feather chevrons inside the button).
"OptionsIcon.tsx": "FeatherSliders",
"PaperClipIcon.tsx": "FeatherPaperclip",
"PencilAltIcon.tsx": "FeatherEdit3",
"PencilEmptyIcon.tsx": "FeatherEdit2",
"PencilIcon.tsx": "FeatherEdit2",
"PlusIcon.tsx": "FeatherPlus",
"PreviewIcon.tsx": "FeatherEye",
"QuestionMarkFilledIcon.tsx": "FeatherHelpCircle",
"RefreshIcon.tsx": "FeatherRefreshCw",
"RightArrowIcon.tsx": "FeatherArrowRight",
"RightIcon.tsx": "FeatherChevronRight",
"SelectorIcon.tsx": "FeatherChevronsUpDown",
"ShareIcon.tsx": "FeatherShare2",
"ShareIconFilled.tsx": "FeatherShare2",
"ShuffleIcon.tsx": "FeatherShuffle",
"ShuffleIconFilled.tsx": "FeatherShuffle",
"SortUpIcon.tsx": "FeatherChevronUp",
"SortUpIconFilled.tsx": "FeatherChevronUp",
"StarsIcon.tsx": "FeatherSparkles",
"StarsIconFilled.tsx": "FeatherSparkles",
"StripeIcon.tsx": "FeatherCreditCard",
"TableIcon.tsx": "FeatherTable",
"TagEmptyIcon.tsx": "FeatherTag",
"TagFilledIcon.tsx": "FeatherTag",
"ThumbsDownIcon.tsx": "FeatherThumbsDown",
"ThumbsDownIconFilled.tsx": "FeatherThumbsDown",
"ThumbsUpEmptyIcon.tsx": "FeatherThumbsUp",
"ThumbsUpFilledIcon.tsx": "FeatherThumbsUp",
"ThumbsUpIcon.tsx": "FeatherThumbsUp",
"ThumbsUpIconFilled.tsx": "FeatherThumbsUp",
"TimeMachineFilled.tsx": "FeatherHistory",
"TimeMachineIcon.tsx": "FeatherHistory",
"TrashEmptyIcon.tsx": "FeatherTrash2",
"TrashIcon.tsx": "FeatherTrash2",
"UpArrow.tsx": "FeatherArrowUp",
"UserGroupIcon.tsx": "FeatherUsers",
"UserGroupIconFilled.tsx": "FeatherUsers",
"UserIcon.tsx": "FeatherUser",
"VariableIcon.tsx": "FeatherVariable",
"ViewBoardsEmptyIcon.tsx": "FeatherColumns",
"ViewBoardsIcon.tsx": "FeatherColumns",
"ViewListIcon.tsx": "FeatherList",
"WebsiteIcon.tsx": "FeatherGlobe",
"WebsiteIconFilled.tsx": "FeatherGlobe",
"XIcon.tsx": "FeatherX",
"YoutubeIcon.tsx": "FeatherYoutube",
// --- ai/ ---
"ai/ChatGptIcon.tsx": "FeatherBot",
// --- crm/ ---
"crm/AddressBookIcon.tsx": "FeatherContact",
"crm/AddressBookIconFilled.tsx": "FeatherContact",
"crm/CompanyIcon.tsx": "FeatherBuilding2",
"crm/CompanyIconFilled.tsx": "FeatherBuilding2",
"crm/FlowIcon.tsx": "FeatherWorkflow",
"crm/FlowIconFilled.tsx": "FeatherWorkflow",
"crm/FormIcon.tsx": "FeatherClipboardList",
"crm/FormIconFilled.tsx": "FeatherClipboardList",
"crm/IncreaseIcon.tsx": "FeatherTrendingUp",
"crm/IncreaseIconFilled.tsx": "FeatherTrendingUp",
"crm/SettingsIcon.tsx": "FeatherSettings",
"crm/SettingsIconFilled.tsx": "FeatherSettings",
"crm/UsDollarCircled.tsx": "FeatherCircleDollarSign",
"crm/UsDollarCircledFilled.tsx": "FeatherCircleDollarSign",
// --- crud/ ---
"crud/DownloadIcon.tsx": "FeatherDownload",
"crud/DownloadIconFilled.tsx": "FeatherDownload",
"crud/EditIcon.tsx": "FeatherEdit2",
"crud/EditIconFilled.tsx": "FeatherEdit2",
"crud/LinkIcon.tsx": "FeatherLink",
"crud/LinkIconFilled.tsx": "FeatherLink",
"crud/PrototypeIcon.tsx": "FeatherBox",
"crud/PrototypeIconFilled.tsx": "FeatherBox",
"crud/ShareIcon.tsx": "FeatherShare2",
"crud/ShareIconFilled.tsx": "FeatherShare2",
"crud/TagsIcon.tsx": "FeatherTags",
"crud/TagsIconFilled.tsx": "FeatherTags",
"crud/UploadIcon.tsx": "FeatherUpload",
"crud/UploadIconFilled.tsx": "FeatherUpload",
// --- emails/ ---
"emails/InboxIcon.tsx": "FeatherInbox",
"emails/InboxIconFilled.tsx": "FeatherInbox",
"emails/MegaphoneIcon.tsx": "FeatherMegaphone",
"emails/MegaphoneIconFilled.tsx": "FeatherMegaphone",
"emails/SentIcon.tsx": "FeatherSend",
"emails/SentIconFilled.tsx": "FeatherSend",
// --- entities/ ---
"entities/ActivityHistoryIcon.tsx": "FeatherActivity",
"entities/ActivityHistoryIconFilled.tsx": "FeatherActivity",
"entities/CodeFileIcon.tsx": "FeatherFileCode",
"entities/CodeFileIconFilled.tsx": "FeatherFileCode",
"entities/CodingIcon.tsx": "FeatherCode",
"entities/CodingIconFilled.tsx": "FeatherCode",
"entities/DocsIcon.tsx": "FeatherFileText",
"entities/DocsIconFilled.tsx": "FeatherFileText",
"entities/FolderIcon.tsx": "FeatherFolder",
"entities/FolderIconFilled.tsx": "FeatherFolder",
"entities/KeyboardIcon.tsx": "FeatherKeyboard",
"entities/KeyboardIconFilled.tsx": "FeatherKeyboard",
"entities/KeyIcon.tsx": "FeatherKey",
"entities/KeyIconFilled.tsx": "FeatherKey",
"entities/ListIcon.tsx": "FeatherList",
"entities/ListIconFilled.tsx": "FeatherList",
"entities/ModulesIcon.tsx": "FeatherPackage",
"entities/ModulesIconFilled.tsx": "FeatherPackage",
"entities/PlaceholderThumbnailXmlIcon.tsx": "FeatherImage",
"entities/PlaceholderThumbnailXmlIconFilled.tsx": "FeatherImage",
"entities/RectangleGroupIcon.tsx": "FeatherLayout",
"entities/RectangleGroupIconFilled.tsx": "FeatherLayout",
"entities/RestApiIcon.tsx": "FeatherServer",
"entities/RestApiIconFilled.tsx": "FeatherServer",
"entities/RowsIcon.tsx": "FeatherRows3",
"entities/RowsIconFilled.tsx": "FeatherRows3",
"entities/TemplateIcon.tsx": "FeatherLayoutTemplate",
"entities/TemplateIconFilled.tsx": "FeatherLayoutTemplate",
"entities/TreeStructureFilledIcon.tsx": "FeatherNetwork",
"entities/TreeStructureIcon.tsx": "FeatherNetwork",
// --- featureFlags/ ---
"featureFlags/ToggleOffIcon.tsx": "FeatherToggleLeft",
"featureFlags/ToggleOnIcon.tsx": "FeatherToggleRight",
// --- knowledgeBase/ ---
"knowledgeBase/BookStackIcon.tsx": "FeatherBookOpen",
"knowledgeBase/BookStackIconFilled.tsx": "FeatherBookOpen",
// --- notifications/ ---
"notifications/AlarmIcon.tsx": "FeatherBell",
"notifications/AlarmIconFilled.tsx": "FeatherBell",
"notifications/ChannelIcon.tsx": "FeatherRadio",
"notifications/ChannelIconFilled.tsx": "FeatherRadio",
"notifications/EarIcon.tsx": "FeatherEar",
"notifications/EarIconFilled.tsx": "FeatherEar",
// --- onboardings/ ---
"onboardings/BycicleIcon.tsx": "FeatherBike",
"onboardings/GoalIcon.tsx": "FeatherTarget",
"onboardings/GoalIconFilled.tsx": "FeatherTarget",
"onboardings/JourneyIcon.tsx": "FeatherRoute",
"onboardings/JourneyIconFilled.tsx": "FeatherRoute",
// --- pages/ ---
"pages/BlockIcon.tsx": "FeatherSquare",
"pages/BlockIconFilled.tsx": "FeatherSquare",
// --- settings/ ---
"settings/AccessDeniedIcon.tsx": "FeatherShieldOff",
"settings/AccessDeniedIconFilled.tsx": "FeatherShieldOff",
"settings/CustomerIcon.tsx": "FeatherUser",
"settings/CustomerIconFilled.tsx": "FeatherUser",
"settings/GroupIcon.tsx": "FeatherUsers",
"settings/GroupIconFilled.tsx": "FeatherUsers",
"settings/MembershipCardIcon.tsx": "FeatherCreditCard",
"settings/MembershipCardIconFilled.tsx": "FeatherCreditCard",
"settings/PeopleIcon.tsx": "FeatherUsers",
"settings/PeopleIconFilled.tsx": "FeatherUsers",
// --- tests/ ---
"tests/ExperimentIcon.tsx": "FeatherFlaskConical",
"tests/ExperimentIconFilled.tsx": "FeatherFlaskConical",
}
const entries = Object.entries(MAP)
const errors = []
// 1) validate every target Feather name actually exists.
for (const [file, feather] of entries) {
if (!featherNames.has(feather)) errors.push(`UNKNOWN FEATHER: ${feather} (for ${file})`)
if (!existsSync(join(iconsRoot, file))) errors.push(`MISSING FILE: ${file}`)
}
if (errors.length) {
console.error("Validation failed:\n" + errors.join("\n"))
process.exit(1)
}
// 2) rewrite every file.
let written = 0
for (const [file, feather] of entries) {
const abs = join(iconsRoot, file)
// helper import path: top-level => "./_feather", nested => "../_feather"
const depth = file.split("/").length - 1
const helperPath = depth === 0 ? "./_feather" : "../".repeat(depth) + "_feather"
const content =
`import { ${feather} } from "@subframe/core"\n\n` +
`import { featherIcon } from "${helperPath}"\n\n` +
`export default featherIcon(${feather})\n`
writeFileSync(abs, content, "utf8")
written++
}
console.log(`Rewrote ${written}/${entries.length} icon files to Feather.`)