288 lines
12 KiB
JavaScript
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.`)
|