Skip to content
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

create vite plugin to treeshake uniconpaths.json #7275

Merged
merged 6 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
94 changes: 94 additions & 0 deletions plugins/treeShakeCareIcons.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Plugin } from "vite";
import * as fs from "fs";
import * as path from "path";
import * as glob from "glob";

/**
* Interface defining options for the treeShakeUniconPathsPlugin.
*
* @interface TreeShakeUniconPathsPluginOptions
* @property {string[]} iconWhitelist - An array of icon names to always include, even if not found in code.
*/

export interface TreeShakeCareIconsOptions {
iconWhitelist: string[];
}

/**
* Creates a Webpack plugin that tree-shakes unused Unicon paths from UniconPaths.json in production builds.
*
* @param {TreeShakeCareIconsOptions} [options] - Optional configuration options. Defaults to an empty iconWhitelist.
* @returns {Plugin} Webpack plugin object.
*/

export function treeShakeCareIcons(
options: TreeShakeCareIconsOptions = { iconWhitelist: [] }
): Plugin {
const rootDir = path.resolve(__dirname, ".."); // update this if moving this code to a different file
const lineIconNameRegex = /"l-[a-z]+(?:-[a-z]+)*"/g;
const allUniconPaths = JSON.parse(
fs.readFileSync(
path.resolve(rootDir, "src/CAREUI/icons/UniconPaths.json"),
"utf8"
)
);

// Extracts icon names from a given file's content.
// Returns an array of icon names like ["l-eye", "l-sync", "l-hearbeat"]
function extractCareIconNames(file: string): string[] {
const fileContent = fs.readFileSync(file, "utf8");

const lineIconNameMatches = fileContent.match(lineIconNameRegex) || [];

const lineIconNames = lineIconNameMatches.map(
(lineIconName) => lineIconName.slice(1, -1) // remove quotes
);

return lineIconNames;
}
// Finds all used icon names within the project's source files (`.tsx` or `.res` extensions).
function getAllUsedIconNames() {
const files = glob.sync(path.resolve(rootDir, "src/**/*.{tsx,res}"));
const usedIconsArray: string[] = [];

files.forEach((file) => {
const iconNames = extractCareIconNames(file);
usedIconsArray.push(...iconNames);
});

return new Set(usedIconsArray);
}
// Generates a map of used icon names to their paths from UniconPaths.json, including any whitelisted icons.
function getTreeShakenUniconPaths() {
const usedIcons = [...getAllUsedIconNames(), ...options.iconWhitelist];
const treeshakenCareIconPaths = {};

for (const iconName of usedIcons) {
const path = allUniconPaths[iconName];
if (path === undefined) {
throw new Error(`Icon ${iconName} is not found in UniconPaths.json`);
} else {
treeshakenCareIconPaths[iconName] = path;
}
}

return treeshakenCareIconPaths;
}

return {
name: "tree-shake-care-icons",
transform(_src, id) {
if (process.env.NODE_ENV !== "production") {
return;
}

// Replace the UniconPaths with the tree-shaken version
if (id.endsWith("UniconPaths.json")) {
return {
code: `export default ${JSON.stringify(getTreeShakenUniconPaths())}`,
map: null,
};
}
},
};
}
4 changes: 2 additions & 2 deletions src/CAREUI/display/RecordMeta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const RecordMeta = ({
{user && !inlineUser && (
<span className="flex items-center gap-1">
by
<CareIcon className="care-l-user" />
<CareIcon icon="l-user" />
{formatName(user)}
{isOnline && (
<div className="h-1.5 w-1.5 rounded-full bg-primary-400" />
Expand All @@ -61,7 +61,7 @@ const RecordMeta = ({
{prefix}
{child}
{user && inlineUser && <span>by</span>}
{user && !inlineUser && <CareIcon className="care-l-user" />}
{user && !inlineUser && <CareIcon icon="l-user" />}
{user && inlineUser && (
<span className="font-medium">{formatName(user)}</span>
)}
Expand Down
2 changes: 1 addition & 1 deletion src/CAREUI/display/SubHeading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function SubHeading(props: Props) {
</span>
{props.lastModified && (
<div className="ml-3 flex flex-row gap-2 text-xs font-medium text-gray-600">
<CareIcon className="care-l-history-alt text-sm" />
<CareIcon icon="l-history-alt" className="text-sm" />
<RecordMeta time={props.lastModified} prefix="Last modified" />
</div>
)}
Expand Down
4 changes: 2 additions & 2 deletions src/CAREUI/icons/CareIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import iconData from "./UniconPaths.json";
export type IconName = keyof typeof iconData;

export interface CareIconProps {
icon?: IconName;
icon: IconName;
className?: string | undefined;
onClick?: React.MouseEventHandler<HTMLSpanElement> | undefined;
id?: string;
Expand All @@ -16,7 +16,7 @@ export interface CareIconProps {
* ### CARE's Official Icon Library.
* @param className icon class name
* @returns icon component
* @example ```<CareIcon className="care-l-hospital" /> ```
* @example ```<CareIcon icon="l-hospital"/> ```
*
* @see [icon library](https://iconscout.com/unicons/)
*/
Expand Down
4 changes: 2 additions & 2 deletions src/CAREUI/interactive/FiltersSlideover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function FiltersSlideover({
onClick={onClear}
id="clear-filter"
>
<CareIcon className="care-l-filter-slash text-lg" />
<CareIcon icon="l-filter-slash" className="text-lg" />
<span>{t("clear")}</span>
</ButtonV2>
<ButtonV2 ghost onClick={onApply} id="apply-filter">
Expand All @@ -62,7 +62,7 @@ export const AdvancedFilterButton = ({ onClick }: { onClick: () => void }) => {
onClick={onClick}
id="advanced-filter"
>
<CareIcon className="care-l-filter" />
<CareIcon icon="l-filter" />
<span className="py-0.5">{t("advanced_filters")}</span>
</ButtonV2>
);
Expand Down
3 changes: 2 additions & 1 deletion src/CAREUI/interactive/LegendInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ export default function LegendInput(props: InputProps) {
onClick={() => setShowPassword(!showPassword)}
>
<CareIcon
className={`care-l-eye${showPassword ? "" : "-slash"} text-lg`}
icon={showPassword ? "l-eye" : "l-eye-slash"}
className="text-lg"
/>
</button>
)}
Expand Down
2 changes: 1 addition & 1 deletion src/CAREUI/interactive/SlideOver.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export default function SlideOver({
onCloseClick && onCloseClick();
}}
>
<CareIcon className="care-l-arrow-left" />
<CareIcon icon="l-arrow-left" />
</button>
<div className="flex w-full">
<h1 className="w-full text-xl font-black">{title}</h1>
Expand Down
2 changes: 1 addition & 1 deletion src/Common/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ export const GENDER: { [key: number]: string } = GENDER_TYPES.reduce(
);

export type CameraPTZ = {
icon?: string;
icon?: IconName;
label: string;
action: string;
loadingLabel?: string;
Expand Down
6 changes: 4 additions & 2 deletions src/Components/ABDM/ABHAProfileModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,13 @@ const ABHAProfileModal = ({ patientId, show, onClose, abha }: IProps) => {
<div className="flex items-center gap-2">
<CareIcon
onClick={() => downloadAbhaCard("pdf")}
className="care-l-print cursor-pointer"
icon="l-print"
className="cursor-pointer"
/>
<CareIcon
onClick={() => downloadAbhaCard("png")}
className="care-l-import cursor-pointer"
icon="l-import"
className="cursor-pointer"
/>
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/Components/ABDM/LinkABHANumberModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default function LinkABHANumberModal({

const title = (
<div className="flex items-center gap-3">
<CareIcon className="care-l-link text-xl" />
<CareIcon icon="l-link" className="text-xl" />
<h2 className="text-xl font-bold text-black">
{currentStep === "ScanExistingQR"
? "Link Existing ABHA Number"
Expand Down Expand Up @@ -752,7 +752,7 @@ const VerifyMobileSection = ({
/>
) : (
<p className="-mt-4 text-sm text-warning-600">
<CareIcon className="care-l-exclamation-triangle h-4 w-4" /> OTP is
<CareIcon icon="l-exclamation-triangle" className="h-4 w-4" /> OTP is
generated if the above phone number is not linked with given Aadhaar
number.
</p>
Expand Down Expand Up @@ -833,7 +833,7 @@ const CreateHealthIDSection = ({
/>

<p className="-mt-4 text-sm text-warning-600">
<CareIcon className="care-l-exclamation-triangle h-4 w-4" /> Existing
<CareIcon icon="l-exclamation-triangle" className="h-4 w-4" /> Existing
ABHA Address is used if ABHA Number already exists.
</p>

Expand Down
23 changes: 12 additions & 11 deletions src/Components/Assets/AssetManage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ const AssetManage = (props: AssetManageProps) => {
<div className="flex grow-0 flex-col md:w-[200px]">
<div className="flex-start flex items-center">
<div className="w-8">
<CareIcon className={`care-l-${item.icon} fill-gray-700 text-lg`} />
<CareIcon icon={item.icon} className="fill-gray-700 text-lg" />
</div>
<div className="break-words text-gray-700">{item.label}</div>
</div>
Expand Down Expand Up @@ -332,7 +332,7 @@ const AssetManage = (props: AssetManageProps) => {
ghost
border
>
<CareIcon className="care-l-export text-lg" />
<CareIcon icon="l-export" className="text-lg" />
Export as JSON
</ButtonV2>
}
Expand All @@ -357,7 +357,8 @@ const AssetManage = (props: AssetManageProps) => {
</span>
<div className="tooltip tooltip-bottom">
<CareIcon
className={`care-l-${assetClassProp.icon} fill-gray-700 text-3xl`}
icon={assetClassProp.icon}
className="fill-gray-700 text-3xl"
/>
<span className="tooltip-text">{assetClassProp.name}</span>
</div>
Expand Down Expand Up @@ -397,17 +398,17 @@ const AssetManage = (props: AssetManageProps) => {
{[
{
label: asset?.location_object.facility.name,
icon: "location-pin-alt",
icon: "l-location-pin-alt",
content: asset?.location_object.name,
},
{
label: "Asset QR Code ID",
icon: "qrcode-scan",
icon: "l-qrcode-scan",
content: asset?.qr_code_id,
},
{
label: "Not working reason",
icon: "exclamation-circle",
icon: "l-exclamation-circle",
content: asset?.not_working_reason,
hide: asset?.is_working,
},
Expand All @@ -425,7 +426,7 @@ const AssetManage = (props: AssetManageProps) => {
data-testid="asset-update-button"
authorizeFor={NonReadOnlyUsers}
>
<CareIcon className="care-l-pen mr-1 h-4" />
<CareIcon icon="l-pen" className="mr-1 h-4" />
{t("update")}
</ButtonV2>
{asset?.asset_class &&
Expand All @@ -442,7 +443,7 @@ const AssetManage = (props: AssetManageProps) => {
id="configure-asset"
data-testid="asset-configure-button"
>
<CareIcon className="care-l-setting h-4" />
<CareIcon icon="l-setting" className="h-4" />
{t("configure")}
</ButtonV2>
)}
Expand All @@ -454,7 +455,7 @@ const AssetManage = (props: AssetManageProps) => {
data-testid="asset-delete-button"
className="inline-flex"
>
<CareIcon className="care-l-trash h-4" />
<CareIcon icon="l-trash" className="h-4" />
<span className="md:hidden">{t("delete")}</span>
</ButtonV2>
)}
Expand All @@ -467,14 +468,14 @@ const AssetManage = (props: AssetManageProps) => {
{[
{
label: "Last serviced on",
icon: "wrench",
icon: "l-wrench",
content:
asset?.last_service?.serviced_on &&
formatDate(asset?.last_service?.serviced_on),
},
{
label: "Notes",
icon: "notes",
icon: "l-notes",
content: asset?.last_service?.note,
},
].map(detailBlock)}
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Assets/AssetType/HL7Monitor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ const HL7Monitor = (props: HL7MonitorProps) => {
error={ipadrdress_error}
/>
<Submit className="w-full">
<CareIcon className="care-l-save" />
<CareIcon icon="l-save" />
<span>Save Configuration</span>
</Submit>
</div>
Expand Down
17 changes: 12 additions & 5 deletions src/Components/Assets/AssetTypes.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { IconName } from "../../CAREUI/icons/CareIcon";
import { BedModel } from "../Facility/models";
import { PerformedByModel } from "../HCX/misc";
import { PatientModel } from "../Patient/models";
Expand Down Expand Up @@ -42,25 +43,31 @@ export const AssetStatus = {
maintenance: "Under Maintenance",
};

export const assetClassProps = {
export const assetClassProps: {
[key in AssetClass]: {
name: string;
description?: string;
icon: IconName;
};
} = {
ONVIF: {
name: "ONVIF Camera",
description: "",
icon: "camera",
icon: "l-camera",
},
HL7MONITOR: {
name: "HL7 Vitals Monitor",
description: "",
icon: "monitor-heart-rate",
icon: "l-monitor-heart-rate",
},
VENTILATOR: {
name: "Ventilator",
description: "",
icon: "lungs",
icon: "l-lungs",
},
NONE: {
name: "N/A",
icon: "box",
icon: "l-box",
},
};

Expand Down
Loading
Loading