@idealyst/components 1.0.83 → 1.0.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +199 -232
- package/README.md +5 -5
- package/package.json +20 -2
- package/plugin/README.md +272 -0
- package/plugin/test-cases.jsx +112 -0
- package/plugin/web-legacy.js +320 -0
- package/plugin/web.js +422 -124
- package/src/Accordion/Accordion.native.tsx +182 -0
- package/src/Accordion/Accordion.styles.tsx +260 -0
- package/src/Accordion/Accordion.web.tsx +147 -0
- package/src/Accordion/index.native.tsx +3 -0
- package/src/Accordion/index.ts +3 -0
- package/src/Accordion/index.web.tsx +3 -0
- package/src/Accordion/types.ts +23 -0
- package/src/ActivityIndicator/ActivityIndicator.native.tsx +17 -12
- package/src/ActivityIndicator/ActivityIndicator.styles.tsx +83 -109
- package/src/ActivityIndicator/ActivityIndicator.web.tsx +23 -17
- package/src/ActivityIndicator/index.ts +5 -2
- package/src/ActivityIndicator/index.web.ts +5 -2
- package/src/ActivityIndicator/types.ts +15 -10
- package/src/Alert/Alert.native.tsx +113 -0
- package/src/Alert/Alert.styles.tsx +304 -0
- package/src/Alert/Alert.web.tsx +123 -0
- package/src/Alert/index.native.ts +5 -0
- package/src/Alert/index.ts +5 -0
- package/src/Alert/index.web.ts +5 -0
- package/src/Alert/types.ts +21 -0
- package/src/Avatar/Avatar.native.tsx +8 -6
- package/src/Avatar/Avatar.styles.tsx +64 -58
- package/src/Avatar/Avatar.web.tsx +13 -8
- package/src/Avatar/index.ts +5 -2
- package/src/Avatar/index.web.ts +5 -2
- package/src/Avatar/types.ts +19 -13
- package/src/Badge/Badge.native.tsx +59 -14
- package/src/Badge/Badge.styles.tsx +125 -139
- package/src/Badge/Badge.web.tsx +72 -16
- package/src/Badge/index.ts +5 -2
- package/src/Badge/index.web.ts +5 -2
- package/src/Badge/types.ts +23 -11
- package/src/Breadcrumb/Breadcrumb.native.tsx +225 -0
- package/src/Breadcrumb/Breadcrumb.styles.tsx +234 -0
- package/src/Breadcrumb/Breadcrumb.web.tsx +268 -0
- package/src/Breadcrumb/index.native.ts +5 -0
- package/src/Breadcrumb/index.ts +5 -0
- package/src/Breadcrumb/index.web.ts +5 -0
- package/src/Breadcrumb/types.ts +56 -0
- package/src/Button/Button.native.tsx +75 -24
- package/src/Button/Button.styles.tsx +248 -205
- package/src/Button/Button.web.tsx +82 -25
- package/src/Button/index.ts +5 -5
- package/src/Button/index.web.ts +5 -3
- package/src/Button/types.ts +32 -15
- package/src/Card/Card.native.tsx +14 -11
- package/src/Card/Card.styles.tsx +146 -220
- package/src/Card/Card.web.tsx +20 -21
- package/src/Card/index.ts +5 -5
- package/src/Card/index.web.ts +5 -3
- package/src/Card/types.ts +24 -17
- package/src/Checkbox/Checkbox.native.tsx +24 -34
- package/src/Checkbox/Checkbox.styles.tsx +223 -275
- package/src/Checkbox/Checkbox.web.tsx +30 -37
- package/src/Checkbox/index.ts +5 -5
- package/src/Checkbox/index.web.ts +5 -3
- package/src/Checkbox/types.ts +26 -20
- package/src/Chip/Chip.native.tsx +126 -0
- package/src/Chip/Chip.styles.tsx +138 -0
- package/src/Chip/Chip.web.tsx +154 -0
- package/src/Chip/index.native.ts +5 -0
- package/src/Chip/index.ts +5 -0
- package/src/Chip/index.web.ts +5 -0
- package/src/Chip/types.ts +51 -0
- package/src/Dialog/Dialog.native.tsx +65 -12
- package/src/Dialog/Dialog.styles.tsx +154 -136
- package/src/Dialog/Dialog.web.tsx +16 -11
- package/src/Dialog/index.ts +5 -2
- package/src/Dialog/index.web.ts +5 -2
- package/src/Dialog/types.ts +22 -16
- package/src/Divider/Divider.native.tsx +19 -14
- package/src/Divider/Divider.styles.tsx +273 -595
- package/src/Divider/Divider.web.tsx +19 -12
- package/src/Divider/index.ts +5 -5
- package/src/Divider/index.web.ts +5 -3
- package/src/Divider/types.ts +28 -19
- package/src/Icon/Icon.native.tsx +17 -24
- package/src/Icon/Icon.styles.tsx +64 -48
- package/src/Icon/Icon.web.tsx +14 -11
- package/src/Icon/IconSvg/IconSvg.native.tsx +42 -0
- package/src/Icon/IconSvg/IconSvg.web.tsx +40 -0
- package/src/Icon/IconSvg/index.native.ts +1 -0
- package/src/Icon/IconSvg/index.ts +1 -0
- package/src/Icon/icon-resolver.native.ts +27 -0
- package/src/Icon/icon-resolver.ts +70 -0
- package/src/Icon/index.ts +5 -5
- package/src/Icon/index.web.ts +5 -3
- package/src/Icon/types.ts +17 -11
- package/src/Image/Image.native.tsx +86 -0
- package/src/Image/Image.styles.tsx +57 -0
- package/src/Image/Image.web.tsx +92 -0
- package/src/Image/index.native.ts +5 -0
- package/src/Image/index.ts +5 -0
- package/src/Image/types.ts +21 -0
- package/src/Input/Input.native.tsx +103 -26
- package/src/Input/Input.styles.tsx +240 -177
- package/src/Input/Input.web.tsx +141 -38
- package/src/Input/index.ts +5 -5
- package/src/Input/index.web.ts +5 -3
- package/src/Input/types.ts +43 -20
- package/src/List/List.native.tsx +56 -0
- package/src/List/List.styles.tsx +257 -0
- package/src/List/List.web.tsx +43 -0
- package/src/List/ListContext.tsx +16 -0
- package/src/List/ListItem.native.tsx +111 -0
- package/src/List/ListItem.web.tsx +110 -0
- package/src/List/ListSection.native.tsx +31 -0
- package/src/List/ListSection.web.tsx +33 -0
- package/src/List/index.native.tsx +5 -0
- package/src/List/index.ts +5 -0
- package/src/List/index.web.tsx +5 -0
- package/src/List/types.ts +42 -0
- package/src/Menu/Menu.native.tsx +150 -0
- package/src/Menu/Menu.styles.tsx +185 -0
- package/src/Menu/Menu.web.tsx +99 -0
- package/src/Menu/MenuItem.native.tsx +66 -0
- package/src/Menu/MenuItem.styles.tsx +119 -0
- package/src/Menu/MenuItem.web.tsx +67 -0
- package/src/Menu/index.native.ts +3 -0
- package/src/Menu/index.ts +3 -0
- package/src/Menu/index.web.ts +3 -0
- package/src/Menu/types.ts +30 -0
- package/src/Popover/Popover.native.tsx +102 -32
- package/src/Popover/Popover.styles.tsx +100 -67
- package/src/Popover/Popover.web.tsx +36 -260
- package/src/Popover/index.ts +5 -2
- package/src/Popover/index.web.ts +5 -2
- package/src/Popover/types.ts +14 -13
- package/src/Pressable/Pressable.native.tsx +7 -6
- package/src/Pressable/Pressable.web.tsx +8 -6
- package/src/Pressable/index.ts +5 -2
- package/src/Pressable/index.web.ts +5 -2
- package/src/Pressable/types.ts +11 -10
- package/src/Progress/Progress.native.tsx +179 -0
- package/src/Progress/Progress.styles.tsx +164 -0
- package/src/Progress/Progress.web.tsx +144 -0
- package/src/Progress/index.native.ts +1 -0
- package/src/Progress/index.ts +5 -0
- package/src/Progress/index.web.ts +5 -0
- package/src/Progress/types.ts +21 -0
- package/src/RadioButton/RadioButton.native.tsx +88 -0
- package/src/RadioButton/RadioButton.styles.tsx +163 -0
- package/src/RadioButton/RadioButton.web.tsx +85 -0
- package/src/RadioButton/RadioGroup.native.tsx +43 -0
- package/src/RadioButton/RadioGroup.web.tsx +49 -0
- package/src/RadioButton/index.native.ts +2 -0
- package/src/RadioButton/index.ts +2 -0
- package/src/RadioButton/index.web.ts +2 -0
- package/src/RadioButton/types.ts +29 -0
- package/src/SVGImage/SVGImage.native.tsx +9 -7
- package/src/SVGImage/SVGImage.styles.tsx +63 -55
- package/src/SVGImage/SVGImage.web.tsx +16 -13
- package/src/SVGImage/index.ts +5 -5
- package/src/SVGImage/index.web.ts +5 -2
- package/src/SVGImage/types.ts +7 -3
- package/src/Screen/Screen.native.tsx +43 -17
- package/src/Screen/Screen.styles.tsx +58 -54
- package/src/Screen/Screen.web.tsx +11 -5
- package/src/Screen/index.ts +5 -2
- package/src/Screen/index.web.ts +5 -2
- package/src/Screen/types.ts +23 -9
- package/src/Select/Select.native.tsx +140 -63
- package/src/Select/Select.styles.tsx +312 -302
- package/src/Select/Select.web.tsx +156 -316
- package/src/Select/index.ts +5 -2
- package/src/Select/index.web.ts +5 -2
- package/src/Select/types.ts +13 -7
- package/src/Skeleton/Skeleton.native.tsx +139 -0
- package/src/Skeleton/Skeleton.styles.tsx +59 -0
- package/src/Skeleton/Skeleton.web.tsx +112 -0
- package/src/Skeleton/index.native.ts +4 -0
- package/src/Skeleton/index.ts +5 -0
- package/src/Skeleton/index.web.ts +5 -0
- package/src/Skeleton/types.ts +75 -0
- package/src/Slider/Slider.native.tsx +248 -0
- package/src/Slider/Slider.styles.tsx +241 -0
- package/src/Slider/Slider.web.tsx +226 -0
- package/src/Slider/index.native.ts +3 -0
- package/src/Slider/index.ts +5 -0
- package/src/Slider/index.web.ts +5 -0
- package/src/Slider/types.ts +31 -0
- package/src/Switch/Switch.native.tsx +131 -0
- package/src/Switch/Switch.styles.tsx +169 -0
- package/src/Switch/Switch.web.tsx +121 -0
- package/src/Switch/index.native.ts +3 -0
- package/src/Switch/index.ts +5 -0
- package/src/Switch/index.web.ts +5 -0
- package/src/Switch/types.ts +21 -0
- package/src/TabBar/TabBar.native.tsx +142 -0
- package/src/TabBar/TabBar.styles.tsx +399 -0
- package/src/TabBar/TabBar.web.tsx +205 -0
- package/src/TabBar/index.native.tsx +3 -0
- package/src/TabBar/index.ts +3 -0
- package/src/TabBar/index.web.tsx +3 -0
- package/src/TabBar/types.ts +26 -0
- package/src/Table/Table.native.tsx +122 -0
- package/src/Table/Table.styles.tsx +283 -0
- package/src/Table/Table.web.tsx +112 -0
- package/src/Table/index.native.tsx +3 -0
- package/src/Table/index.ts +3 -0
- package/src/Table/index.web.tsx +3 -0
- package/src/Table/types.ts +28 -0
- package/src/Text/Text.native.tsx +12 -11
- package/src/Text/Text.styles.tsx +76 -64
- package/src/Text/Text.web.tsx +14 -9
- package/src/Text/index.ts +5 -5
- package/src/Text/index.web.ts +5 -3
- package/src/Text/types.ts +20 -13
- package/src/TextArea/TextArea.native.tsx +134 -0
- package/src/TextArea/TextArea.styles.tsx +175 -0
- package/src/TextArea/TextArea.web.tsx +156 -0
- package/src/TextArea/index.native.ts +3 -0
- package/src/TextArea/index.ts +3 -0
- package/src/TextArea/index.web.ts +3 -0
- package/src/TextArea/types.ts +30 -0
- package/src/Tooltip/Tooltip.native.tsx +165 -0
- package/src/Tooltip/Tooltip.styles.tsx +73 -0
- package/src/Tooltip/Tooltip.web.tsx +87 -0
- package/src/Tooltip/index.native.ts +3 -0
- package/src/Tooltip/index.ts +3 -0
- package/src/Tooltip/types.ts +18 -0
- package/src/Video/Video.native.tsx +105 -0
- package/src/Video/Video.styles.tsx +39 -0
- package/src/Video/Video.web.tsx +115 -0
- package/src/Video/index.native.ts +5 -0
- package/src/Video/index.ts +5 -0
- package/src/Video/types.ts +29 -0
- package/src/View/View.native.tsx +9 -14
- package/src/View/View.styles.tsx +101 -93
- package/src/View/View.web.tsx +16 -17
- package/src/View/index.ts +5 -5
- package/src/View/index.web.ts +5 -3
- package/src/View/types.ts +29 -21
- package/src/examples/AccordionExamples.tsx +126 -0
- package/src/examples/AlertExamples.tsx +280 -0
- package/src/examples/AvatarExamples.tsx +23 -23
- package/src/examples/BadgeExamples.tsx +109 -41
- package/src/examples/BreadcrumbExamples.tsx +312 -0
- package/src/examples/ButtonExamples.tsx +160 -33
- package/src/examples/CardExamples.tsx +40 -40
- package/src/examples/CheckboxExamples.tsx +12 -12
- package/src/examples/ChipExamples.tsx +197 -0
- package/src/examples/DialogExamples.tsx +22 -22
- package/src/examples/DividerExamples.tsx +49 -49
- package/src/examples/IconExamples.tsx +270 -54
- package/src/examples/ImageExamples.tsx +174 -0
- package/src/examples/InputExamples.tsx +75 -17
- package/src/examples/ListExamples.tsx +288 -0
- package/src/examples/MenuExamples.tsx +144 -0
- package/src/examples/PopoverExamples.tsx +69 -73
- package/src/examples/ProgressExamples.tsx +137 -0
- package/src/examples/RadioButtonExamples.tsx +161 -0
- package/src/examples/SVGImageExamples.tsx +19 -17
- package/src/examples/ScreenExamples.tsx +31 -31
- package/src/examples/SelectExamples.tsx +67 -67
- package/src/examples/SkeletonExamples.tsx +206 -0
- package/src/examples/SliderExamples.tsx +200 -0
- package/src/examples/SwitchExamples.tsx +182 -0
- package/src/examples/TabBarExamples.tsx +143 -0
- package/src/examples/TableExamples.tsx +280 -0
- package/src/examples/TextAreaExamples.tsx +173 -0
- package/src/examples/TextExamples.tsx +28 -32
- package/src/examples/ThemeExtensionExamples.tsx +10 -10
- package/src/examples/TooltipExamples.tsx +126 -0
- package/src/examples/VideoExamples.tsx +144 -0
- package/src/examples/ViewExamples.tsx +64 -56
- package/src/examples/index.ts +17 -3
- package/src/hooks/useMergeRefs.ts +16 -0
- package/src/hooks/useSmartPosition.native.ts +169 -0
- package/src/index.native.ts +80 -9
- package/src/index.ts +71 -1
- package/src/internal/BoundedModalContent.native.tsx +58 -0
- package/src/internal/PositionedPortal.tsx +254 -0
- package/src/internal/SafeAreaDebugOverlay.native.tsx +173 -0
- package/src/unistyles.d.ts +6 -0
- package/src/utils/buildSizeVariants.ts +16 -0
- package/src/utils/deepMerge.ts +43 -0
- package/src/utils/positionUtils.native.ts +280 -0
- package/src/utils/styleHelpers.ts +48 -0
- package/LLM-ACCESS-GUIDE.md +0 -143
- package/src/ActivityIndicator/README.md +0 -132
- package/src/Avatar/README.md +0 -139
- package/src/Badge/README.md +0 -170
- package/src/Button/Button.types.ts +0 -12
- package/src/Button/README.md +0 -262
- package/src/Card/README.md +0 -258
- package/src/Checkbox/README.md +0 -102
- package/src/Dialog/README.md +0 -210
- package/src/Divider/README.md +0 -108
- package/src/Icon/README.md +0 -81
- package/src/Input/README.md +0 -100
- package/src/SVGImage/README.md +0 -209
- package/src/Screen/README.md +0 -86
- package/src/Select/README.md +0 -166
- package/src/Text/README.md +0 -94
- package/src/View/README.md +0 -107
- package/src/examples/AllExamples.tsx +0 -88
- package/src/examples/README.md +0 -136
- package/src/examples/ValidationExamples.tsx +0 -95
- package/src/examples/extendedTheme.ts +0 -329
- package/src/theme/breakpoints.ts +0 -8
- package/src/theme/colorResolver.ts +0 -218
- package/src/theme/colors.ts +0 -315
- package/src/theme/defaultThemes.ts +0 -326
- package/src/theme/index.ts +0 -188
- package/src/theme/themeBuilder.ts +0 -602
- package/src/theme/unistyles.d.ts +0 -6
- package/src/theme/variantHelpers.ts +0 -584
- package/src/theme/variants.ts +0 -56
package/plugin/web.js
CHANGED
|
@@ -1,150 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced MDI Auto-Import Babel Plugin v2.0
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* 1. Context-aware string replacement - only transforms strings used with Icon component
|
|
6
|
+
* 2. Namespace prefix support - "mdi:iconname" guarantees transformation
|
|
7
|
+
* 3. Variable tracking with scope analysis - follows variables back to their declarations
|
|
8
|
+
* 4. No false positives - only transforms Icon-related strings
|
|
9
|
+
* 5. Button/Badge/Breadcrumb/Menu icon prop transformation with path injection
|
|
10
|
+
*/
|
|
11
|
+
|
|
1
12
|
module.exports = function ({ types: t }, options = {}) {
|
|
2
13
|
const debug = options.debug || false;
|
|
3
14
|
const manifestPath = options.manifestPath || './icons.manifest.json';
|
|
4
|
-
|
|
15
|
+
|
|
5
16
|
// Debug logging function that only logs when debug is enabled
|
|
6
17
|
const debugLog = (...args) => {
|
|
7
18
|
if (debug) {
|
|
8
19
|
console.log(...args);
|
|
9
20
|
}
|
|
10
21
|
};
|
|
11
|
-
|
|
12
|
-
debugLog('[mdi-auto-import] Plugin loaded');
|
|
13
|
-
|
|
22
|
+
|
|
23
|
+
debugLog('[mdi-auto-import-enhanced] Plugin loaded');
|
|
24
|
+
|
|
14
25
|
const importedIcons = new Set();
|
|
15
26
|
const iconImportIdentifiers = new Map();
|
|
16
27
|
let hasIconImport = false;
|
|
17
28
|
let manifestIcons = new Set();
|
|
18
|
-
|
|
29
|
+
|
|
30
|
+
// Track variables that are used with Icon component
|
|
31
|
+
const iconRelatedVariables = new Set();
|
|
32
|
+
|
|
19
33
|
// Load icon manifest if it exists
|
|
20
34
|
function loadIconManifest() {
|
|
21
35
|
try {
|
|
22
36
|
const fs = require('fs');
|
|
23
37
|
const path = require('path');
|
|
24
|
-
|
|
25
|
-
// Try to resolve the manifest path relative to the current working directory
|
|
38
|
+
|
|
26
39
|
const fullPath = path.resolve(process.cwd(), manifestPath);
|
|
27
|
-
|
|
40
|
+
|
|
28
41
|
if (fs.existsSync(fullPath)) {
|
|
29
42
|
const manifestContent = fs.readFileSync(fullPath, 'utf8');
|
|
30
43
|
const manifest = JSON.parse(manifestContent);
|
|
31
|
-
|
|
44
|
+
|
|
32
45
|
if (manifest.icons && Array.isArray(manifest.icons)) {
|
|
33
46
|
manifest.icons.forEach(iconName => {
|
|
34
47
|
if (typeof iconName === 'string') {
|
|
35
48
|
manifestIcons.add(iconName);
|
|
36
49
|
}
|
|
37
50
|
});
|
|
38
|
-
debugLog(`[mdi-auto-import] Loaded ${manifestIcons.size} icons from manifest: ${fullPath}`);
|
|
39
|
-
debugLog('[mdi-auto-import] Manifest icons:', Array.from(manifestIcons));
|
|
51
|
+
debugLog(`[mdi-auto-import-enhanced] Loaded ${manifestIcons.size} icons from manifest: ${fullPath}`);
|
|
40
52
|
} else {
|
|
41
|
-
console.warn(`[mdi-auto-import] Invalid manifest format in ${fullPath}. Expected { "icons": ["icon-name", ...] }`);
|
|
53
|
+
console.warn(`[mdi-auto-import-enhanced] Invalid manifest format in ${fullPath}. Expected { "icons": ["icon-name", ...] }`);
|
|
42
54
|
}
|
|
43
|
-
} else {
|
|
44
|
-
debugLog(`[mdi-auto-import] No manifest found at ${fullPath}`);
|
|
45
55
|
}
|
|
46
56
|
} catch (error) {
|
|
47
|
-
console.warn(`[mdi-auto-import] Error loading manifest from ${manifestPath}: ${error.message}`);
|
|
57
|
+
console.warn(`[mdi-auto-import-enhanced] Error loading manifest from ${manifestPath}: ${error.message}`);
|
|
48
58
|
}
|
|
49
59
|
}
|
|
50
|
-
|
|
60
|
+
|
|
51
61
|
function formatIconName(name) {
|
|
52
|
-
// Handle empty or invalid names
|
|
53
62
|
if (!name || typeof name !== 'string') {
|
|
54
63
|
throw new Error(`Invalid icon name: ${name}`);
|
|
55
64
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
|
|
66
|
+
// Strip mdi: prefix if it exists (safety check, should already be stripped)
|
|
67
|
+
const cleanName = name.startsWith('mdi:') ? name.substring(4) : name;
|
|
68
|
+
|
|
69
|
+
// Check if the name contains only valid characters (letters, numbers, hyphens, underscores)
|
|
70
|
+
if (!/^[a-zA-Z0-9-_]+$/.test(cleanName)) {
|
|
71
|
+
debugLog(`[mdi-auto-import-enhanced] formatIconName: Invalid icon name "${name}" (contains special characters), using "help-circle" as fallback`);
|
|
72
|
+
return 'HelpCircle';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const formatted = cleanName
|
|
76
|
+
.replace(/[-_:]/g, ' ') // Also handle colons
|
|
60
77
|
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
61
78
|
.split(' ')
|
|
62
79
|
.filter(part => part.length > 0)
|
|
63
80
|
.map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
64
81
|
.join('');
|
|
65
|
-
|
|
66
|
-
debugLog(`[mdi-auto-import] formatIconName: ${name} -> ${formatted}`);
|
|
82
|
+
|
|
83
|
+
debugLog(`[mdi-auto-import-enhanced] formatIconName: ${name} -> ${formatted}`);
|
|
67
84
|
return formatted;
|
|
68
85
|
}
|
|
69
|
-
|
|
86
|
+
|
|
70
87
|
function getMdiIconName(name) {
|
|
71
|
-
|
|
72
|
-
|
|
88
|
+
// First ensure the name has mdi: prefix stripped (safety check)
|
|
89
|
+
const cleanName = name.startsWith('mdi:') ? name.substring(4) : name;
|
|
90
|
+
const mdiName = `mdi${formatIconName(cleanName)}`;
|
|
91
|
+
debugLog(`[mdi-auto-import-enhanced] getMdiIconName: ${name} -> ${mdiName}`);
|
|
73
92
|
return mdiName;
|
|
74
93
|
}
|
|
75
|
-
|
|
94
|
+
|
|
76
95
|
function getIconIdentifier(iconName) {
|
|
77
96
|
if (!iconImportIdentifiers.has(iconName)) {
|
|
78
|
-
|
|
97
|
+
// Sanitize the icon name to create a valid JavaScript identifier
|
|
98
|
+
// Remove any characters that aren't valid in identifiers
|
|
99
|
+
const sanitized = iconName.replace(/[^a-zA-Z0-9_$]/g, '');
|
|
100
|
+
iconImportIdentifiers.set(iconName, `_${sanitized}`);
|
|
79
101
|
}
|
|
80
102
|
const identifier = iconImportIdentifiers.get(iconName);
|
|
81
|
-
debugLog(`[mdi-auto-import] getIconIdentifier: ${iconName} -> ${identifier}`);
|
|
103
|
+
debugLog(`[mdi-auto-import-enhanced] getIconIdentifier: ${iconName} -> ${identifier}`);
|
|
82
104
|
return identifier;
|
|
83
105
|
}
|
|
84
106
|
|
|
85
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Extract icon name from string, handling namespace prefix
|
|
109
|
+
* Returns null if not a valid icon string
|
|
110
|
+
*/
|
|
111
|
+
function extractIconName(str) {
|
|
112
|
+
if (!str || typeof str !== 'string') return null;
|
|
113
|
+
|
|
114
|
+
// Handle namespace prefix: "mdi:home" -> "home"
|
|
115
|
+
if (str.startsWith('mdi:')) {
|
|
116
|
+
return str.substring(4);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return str;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Check if a string literal is icon-related based on context
|
|
124
|
+
*/
|
|
125
|
+
function isIconRelatedString(path, stringValue) {
|
|
126
|
+
// Always transform if it has the mdi: prefix
|
|
127
|
+
if (stringValue.startsWith('mdi:')) {
|
|
128
|
+
debugLog(`[mdi-auto-import-enhanced] String "${stringValue}" has mdi: prefix - will transform`);
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Check if this string is in the manifest
|
|
133
|
+
if (manifestIcons.has(stringValue)) {
|
|
134
|
+
debugLog(`[mdi-auto-import-enhanced] String "${stringValue}" is in manifest - will transform`);
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Icon prop names to check
|
|
139
|
+
const iconPropNames = ['name', 'leftIcon', 'rightIcon', 'icon'];
|
|
140
|
+
|
|
141
|
+
// Walk up the tree to find context
|
|
142
|
+
let currentPath = path;
|
|
143
|
+
while (currentPath) {
|
|
144
|
+
const node = currentPath.node;
|
|
145
|
+
|
|
146
|
+
// Check if we're in a JSXAttribute with an icon-related prop name
|
|
147
|
+
if (t.isJSXAttribute(currentPath.parent)) {
|
|
148
|
+
const attr = currentPath.parent;
|
|
149
|
+
if (t.isJSXIdentifier(attr.name) && iconPropNames.includes(attr.name.name)) {
|
|
150
|
+
// Now check if the parent JSXOpeningElement is Icon, Button, Badge, etc.
|
|
151
|
+
const openingElement = currentPath.parentPath.parent;
|
|
152
|
+
if (t.isJSXOpeningElement(openingElement)) {
|
|
153
|
+
if (t.isJSXIdentifier(openingElement.name)) {
|
|
154
|
+
const componentName = openingElement.name.name;
|
|
155
|
+
debugLog(`[mdi-auto-import-enhanced] String "${stringValue}" is in ${componentName}.${attr.name.name} prop - will transform`);
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Check if we're assigned to a variable that's used with Icon
|
|
163
|
+
if (t.isVariableDeclarator(currentPath.parent)) {
|
|
164
|
+
const declarator = currentPath.parent;
|
|
165
|
+
if (t.isIdentifier(declarator.id)) {
|
|
166
|
+
const varName = declarator.id.name;
|
|
167
|
+
if (iconRelatedVariables.has(varName)) {
|
|
168
|
+
debugLog(`[mdi-auto-import-enhanced] String "${stringValue}" is in icon-related variable "${varName}" - will transform`);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
currentPath = currentPath.parentPath;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Track variables that are used with Icon component
|
|
182
|
+
* This runs in a first pass to identify which variables are icon-related
|
|
183
|
+
*/
|
|
184
|
+
function trackIconRelatedVariables(programPath) {
|
|
185
|
+
// Components that accept icon props
|
|
186
|
+
const componentsWithIconProps = {
|
|
187
|
+
'Icon': ['name'],
|
|
188
|
+
'Button': ['leftIcon', 'rightIcon'],
|
|
189
|
+
'Badge': ['icon'],
|
|
190
|
+
'Breadcrumb': ['icon'],
|
|
191
|
+
'Menu': ['icon']
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
programPath.traverse({
|
|
195
|
+
JSXElement(path) {
|
|
196
|
+
const { node } = path;
|
|
197
|
+
|
|
198
|
+
if (t.isJSXIdentifier(node.openingElement.name)) {
|
|
199
|
+
const componentName = node.openingElement.name.name;
|
|
200
|
+
const iconProps = componentsWithIconProps[componentName];
|
|
201
|
+
|
|
202
|
+
if (iconProps) {
|
|
203
|
+
// Find all icon-related attributes
|
|
204
|
+
iconProps.forEach(propName => {
|
|
205
|
+
const attr = node.openingElement.attributes.find(attr =>
|
|
206
|
+
t.isJSXAttribute(attr) &&
|
|
207
|
+
t.isJSXIdentifier(attr.name) &&
|
|
208
|
+
attr.name.name === propName
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
if (attr && t.isJSXExpressionContainer(attr.value)) {
|
|
212
|
+
const expression = attr.value.expression;
|
|
213
|
+
|
|
214
|
+
// Track any identifiers used in the icon prop
|
|
215
|
+
if (t.isIdentifier(expression)) {
|
|
216
|
+
iconRelatedVariables.add(expression.name);
|
|
217
|
+
debugLog(`[mdi-auto-import-enhanced] Tracked icon-related variable: ${expression.name} (from ${componentName}.${propName})`);
|
|
218
|
+
|
|
219
|
+
// Follow the binding to find its declaration
|
|
220
|
+
const binding = path.scope.getBinding(expression.name);
|
|
221
|
+
if (binding && binding.path.isVariableDeclarator()) {
|
|
222
|
+
const init = binding.path.node.init;
|
|
223
|
+
if (t.isStringLiteral(init)) {
|
|
224
|
+
debugLog(`[mdi-auto-import-enhanced] Variable ${expression.name} = "${init.value}"`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Track variables in ternaries and logical expressions
|
|
230
|
+
path.traverse({
|
|
231
|
+
Identifier(idPath) {
|
|
232
|
+
// Only track top-level identifiers, not property accesses
|
|
233
|
+
if (!t.isMemberExpression(idPath.parent)) {
|
|
234
|
+
iconRelatedVariables.add(idPath.node.name);
|
|
235
|
+
debugLog(`[mdi-auto-import-enhanced] Tracked icon-related variable in expression: ${idPath.node.name} (from ${componentName}.${propName})`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Extract all icon names from an expression, now with context awareness
|
|
249
|
+
*/
|
|
86
250
|
function extractIconNames(expression, path) {
|
|
87
251
|
const iconNames = new Set();
|
|
88
|
-
|
|
89
|
-
function traverse(node) {
|
|
252
|
+
|
|
253
|
+
function traverse(node, nodePath) {
|
|
90
254
|
if (!node) return;
|
|
91
|
-
|
|
255
|
+
|
|
92
256
|
if (t.isStringLiteral(node)) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
257
|
+
const iconName = extractIconName(node.value);
|
|
258
|
+
if (iconName) {
|
|
259
|
+
iconNames.add(iconName);
|
|
260
|
+
debugLog(`[mdi-auto-import-enhanced] Found icon name: ${iconName} (from "${node.value}")`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
96
263
|
else if (t.isConditionalExpression(node)) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
traverse(node.
|
|
100
|
-
traverse(node.alternate);
|
|
264
|
+
debugLog('[mdi-auto-import-enhanced] Processing conditional expression');
|
|
265
|
+
traverse(node.consequent, nodePath);
|
|
266
|
+
traverse(node.alternate, nodePath);
|
|
101
267
|
}
|
|
102
268
|
else if (t.isLogicalExpression(node)) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
traverse(node.
|
|
106
|
-
traverse(node.right);
|
|
269
|
+
debugLog('[mdi-auto-import-enhanced] Processing logical expression');
|
|
270
|
+
traverse(node.left, nodePath);
|
|
271
|
+
traverse(node.right, nodePath);
|
|
107
272
|
}
|
|
108
273
|
else if (t.isTemplateLiteral(node)) {
|
|
109
|
-
// Handle template literals with no expressions (static strings)
|
|
110
274
|
if (node.expressions.length === 0 && node.quasis.length === 1) {
|
|
111
275
|
const value = node.quasis[0].value.cooked;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
else if (t.isMemberExpression(node)) {
|
|
119
|
-
// Handle object.property where object is static
|
|
120
|
-
if (t.isIdentifier(node.object) && t.isIdentifier(node.property)) {
|
|
121
|
-
debugLog(`[mdi-auto-import] Found member expression: ${node.object.name}.${node.property.name}`);
|
|
122
|
-
// We could potentially resolve this if we track object declarations
|
|
123
|
-
// For now, just warn that we found it but can't resolve it
|
|
276
|
+
const iconName = extractIconName(value);
|
|
277
|
+
if (iconName) {
|
|
278
|
+
iconNames.add(iconName);
|
|
279
|
+
debugLog(`[mdi-auto-import-enhanced] Found icon name in template literal: ${iconName}`);
|
|
280
|
+
}
|
|
124
281
|
}
|
|
125
282
|
}
|
|
126
|
-
else if (t.isCallExpression(node)) {
|
|
127
|
-
debugLog('[mdi-auto-import] Found function call - cannot statically analyze');
|
|
128
|
-
console.warn(`[mdi-auto-import] Function call detected at ${path.node.loc ? `${path.node.loc.start.line}:${path.node.loc.start.column}` : 'unknown location'}. Consider adding icon names to manifest (${manifestPath}) for auto-import support.`);
|
|
129
|
-
// For function calls, we can't statically determine the result
|
|
130
|
-
// But we could potentially add runtime analysis or hints
|
|
131
|
-
}
|
|
132
283
|
else if (t.isIdentifier(node)) {
|
|
133
|
-
debugLog(`[mdi-auto-import]
|
|
134
|
-
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
284
|
+
debugLog(`[mdi-auto-import-enhanced] Following identifier: ${node.name}`);
|
|
285
|
+
|
|
286
|
+
// Try to resolve the identifier to its value
|
|
287
|
+
const binding = path.scope.getBinding(node.name);
|
|
288
|
+
if (binding && binding.path.isVariableDeclarator()) {
|
|
289
|
+
const init = binding.path.node.init;
|
|
290
|
+
if (init) {
|
|
291
|
+
traverse(init, binding.path);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
139
294
|
}
|
|
140
295
|
}
|
|
141
|
-
|
|
142
|
-
traverse(expression);
|
|
296
|
+
|
|
297
|
+
traverse(expression, path);
|
|
143
298
|
return Array.from(iconNames);
|
|
144
299
|
}
|
|
145
300
|
|
|
146
301
|
return {
|
|
147
|
-
name: 'mdi-auto-import',
|
|
302
|
+
name: 'mdi-auto-import-enhanced',
|
|
148
303
|
visitor: {
|
|
149
304
|
Program: {
|
|
150
305
|
enter(path) {
|
|
@@ -153,30 +308,34 @@ module.exports = function ({ types: t }, options = {}) {
|
|
|
153
308
|
iconImportIdentifiers.clear();
|
|
154
309
|
hasIconImport = false;
|
|
155
310
|
manifestIcons.clear();
|
|
156
|
-
|
|
311
|
+
iconRelatedVariables.clear();
|
|
312
|
+
|
|
157
313
|
// Load icon manifest
|
|
158
314
|
loadIconManifest();
|
|
159
|
-
|
|
315
|
+
|
|
160
316
|
// Add all manifest icons to the import list
|
|
161
317
|
manifestIcons.forEach(iconName => {
|
|
162
318
|
try {
|
|
163
319
|
const mdiIconName = getMdiIconName(iconName);
|
|
164
320
|
importedIcons.add(mdiIconName);
|
|
165
|
-
debugLog(`[mdi-auto-import] Added manifest icon to import list: ${mdiIconName}`);
|
|
321
|
+
debugLog(`[mdi-auto-import-enhanced] Added manifest icon to import list: ${mdiIconName}`);
|
|
166
322
|
} catch (error) {
|
|
167
|
-
console.error(`[mdi-auto-import] Error processing manifest icon "${iconName}": ${error.message}`);
|
|
323
|
+
console.error(`[mdi-auto-import-enhanced] Error processing manifest icon "${iconName}": ${error.message}`);
|
|
168
324
|
}
|
|
169
325
|
});
|
|
170
|
-
|
|
326
|
+
|
|
327
|
+
// First pass: track which variables are icon-related
|
|
328
|
+
trackIconRelatedVariables(path);
|
|
329
|
+
|
|
171
330
|
// Check if Icon is already imported from @mdi/react
|
|
172
331
|
path.node.body.forEach(node => {
|
|
173
332
|
if (t.isImportDeclaration(node) && node.source.value === '@mdi/react') {
|
|
174
|
-
debugLog('[mdi-auto-import] Found @mdi/react import');
|
|
175
|
-
const hasIconSpecifier = node.specifiers.some(spec =>
|
|
333
|
+
debugLog('[mdi-auto-import-enhanced] Found @mdi/react import');
|
|
334
|
+
const hasIconSpecifier = node.specifiers.some(spec =>
|
|
176
335
|
t.isImportDefaultSpecifier(spec) && spec.local.name === 'MdiIcon'
|
|
177
336
|
);
|
|
178
337
|
if (hasIconSpecifier) {
|
|
179
|
-
debugLog('[mdi-auto-import] MdiIcon already imported');
|
|
338
|
+
debugLog('[mdi-auto-import-enhanced] MdiIcon already imported');
|
|
180
339
|
hasIconImport = true;
|
|
181
340
|
}
|
|
182
341
|
}
|
|
@@ -186,12 +345,12 @@ module.exports = function ({ types: t }, options = {}) {
|
|
|
186
345
|
if (importedIcons.size === 0) {
|
|
187
346
|
return;
|
|
188
347
|
}
|
|
189
|
-
debugLog(`[mdi-auto-import] importedIcons.size: ${importedIcons.size}`);
|
|
190
|
-
|
|
348
|
+
debugLog(`[mdi-auto-import-enhanced] importedIcons.size: ${importedIcons.size}`);
|
|
349
|
+
|
|
191
350
|
// Add imports at the top of the file if any icons were used
|
|
192
351
|
if (importedIcons.size > 0) {
|
|
193
|
-
debugLog('[mdi-auto-import] Adding imports for icons:', Array.from(importedIcons));
|
|
194
|
-
|
|
352
|
+
debugLog('[mdi-auto-import-enhanced] Adding imports for icons:', Array.from(importedIcons));
|
|
353
|
+
|
|
195
354
|
// Import individual icons from @mdi/js
|
|
196
355
|
const iconImportSpecifiers = Array.from(importedIcons).map(iconName => {
|
|
197
356
|
const identifier = getIconIdentifier(iconName);
|
|
@@ -200,121 +359,260 @@ module.exports = function ({ types: t }, options = {}) {
|
|
|
200
359
|
t.identifier(iconName)
|
|
201
360
|
);
|
|
202
361
|
});
|
|
203
|
-
|
|
362
|
+
|
|
204
363
|
const iconImportDeclaration = t.importDeclaration(
|
|
205
364
|
iconImportSpecifiers,
|
|
206
365
|
t.stringLiteral('@mdi/js')
|
|
207
366
|
);
|
|
208
|
-
|
|
367
|
+
|
|
209
368
|
// Import Icon component from @mdi/react if not already imported
|
|
210
369
|
if (!hasIconImport) {
|
|
211
|
-
debugLog('[mdi-auto-import] Adding MdiIcon import from @mdi/react');
|
|
370
|
+
debugLog('[mdi-auto-import-enhanced] Adding MdiIcon import from @mdi/react');
|
|
212
371
|
const iconComponentImport = t.importDeclaration(
|
|
213
372
|
[t.importDefaultSpecifier(t.identifier('MdiIcon'))],
|
|
214
373
|
t.stringLiteral('@mdi/react')
|
|
215
374
|
);
|
|
216
375
|
path.unshiftContainer('body', iconComponentImport);
|
|
217
376
|
} else {
|
|
218
|
-
debugLog('[mdi-auto-import] MdiIcon already imported, skipping');
|
|
377
|
+
debugLog('[mdi-auto-import-enhanced] MdiIcon already imported, skipping');
|
|
219
378
|
}
|
|
220
|
-
|
|
379
|
+
|
|
221
380
|
// Add icon imports
|
|
222
381
|
path.unshiftContainer('body', iconImportDeclaration);
|
|
223
|
-
debugLog('[mdi-auto-import] Imports added successfully');
|
|
382
|
+
debugLog('[mdi-auto-import-enhanced] Imports added successfully');
|
|
224
383
|
} else {
|
|
225
|
-
debugLog('[mdi-auto-import] No icons to import');
|
|
384
|
+
debugLog('[mdi-auto-import-enhanced] No icons to import');
|
|
226
385
|
}
|
|
227
386
|
}
|
|
228
387
|
},
|
|
229
|
-
|
|
388
|
+
|
|
230
389
|
JSXElement(path) {
|
|
231
390
|
const { node } = path;
|
|
232
|
-
|
|
391
|
+
|
|
392
|
+
// Handle Badge, Button, Breadcrumb, Menu components with icon props
|
|
393
|
+
if (t.isJSXIdentifier(node.openingElement.name)) {
|
|
394
|
+
const componentName = node.openingElement.name.name;
|
|
395
|
+
const iconPropMapping = {
|
|
396
|
+
'Button': { props: ['leftIcon', 'rightIcon'], pathProps: ['leftIconPath', 'rightIconPath'] },
|
|
397
|
+
'Badge': { props: ['icon'], pathProps: ['iconPath'] },
|
|
398
|
+
'Breadcrumb': { props: ['icon'], pathProps: ['iconPath'] },
|
|
399
|
+
'Menu': { props: ['icon'], pathProps: ['iconPath'] }
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
const iconConfig = iconPropMapping[componentName];
|
|
403
|
+
if (iconConfig) {
|
|
404
|
+
debugLog(`[mdi-auto-import-enhanced] JSXElement visitor - Found ${componentName}`);
|
|
405
|
+
|
|
406
|
+
// Process each icon prop
|
|
407
|
+
iconConfig.props.forEach((propName, index) => {
|
|
408
|
+
const attr = node.openingElement.attributes.find(attr =>
|
|
409
|
+
t.isJSXAttribute(attr) &&
|
|
410
|
+
t.isJSXIdentifier(attr.name) &&
|
|
411
|
+
attr.name.name === propName
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
if (attr && t.isStringLiteral(attr.value)) {
|
|
415
|
+
const stringValue = attr.value.value;
|
|
416
|
+
const iconName = extractIconName(stringValue);
|
|
417
|
+
|
|
418
|
+
if (iconName) {
|
|
419
|
+
debugLog(`[mdi-auto-import-enhanced] - Found ${propName}="${stringValue}" (StringLiteral)`);
|
|
420
|
+
|
|
421
|
+
try {
|
|
422
|
+
const mdiIconName = getMdiIconName(iconName);
|
|
423
|
+
const iconIdentifier = getIconIdentifier(mdiIconName);
|
|
424
|
+
importedIcons.add(mdiIconName);
|
|
425
|
+
debugLog(`[mdi-auto-import-enhanced] - Added icon from ${componentName}.${propName}: ${mdiIconName}`);
|
|
426
|
+
|
|
427
|
+
// Add the corresponding path prop (e.g., leftIconPath, rightIconPath, iconPath)
|
|
428
|
+
const pathPropName = iconConfig.pathProps[index];
|
|
429
|
+
const pathAttr = t.jsxAttribute(
|
|
430
|
+
t.jsxIdentifier(pathPropName),
|
|
431
|
+
t.jsxExpressionContainer(t.identifier(iconIdentifier))
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
// Add the path attribute to the component
|
|
435
|
+
node.openingElement.attributes.push(pathAttr);
|
|
436
|
+
debugLog(`[mdi-auto-import-enhanced] - Added ${pathPropName}={${iconIdentifier}} to ${componentName}`);
|
|
437
|
+
} catch (error) {
|
|
438
|
+
console.error(`[mdi-auto-import-enhanced] Error processing icon "${iconName}" from ${componentName}.${propName}: ${error.message}`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
} else if (attr) {
|
|
442
|
+
const attrType = attr.value?.type || 'unknown';
|
|
443
|
+
debugLog(`[mdi-auto-import-enhanced] - Found ${propName} (${attrType})`);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
233
449
|
// Check if this is an Icon component from @idealyst/components
|
|
234
450
|
if (
|
|
235
451
|
t.isJSXIdentifier(node.openingElement.name) &&
|
|
236
452
|
node.openingElement.name.name === 'Icon'
|
|
237
453
|
) {
|
|
238
|
-
|
|
454
|
+
|
|
239
455
|
// Find the name attribute
|
|
240
|
-
const nameAttr = node.openingElement.attributes.find(attr =>
|
|
241
|
-
t.isJSXAttribute(attr) &&
|
|
242
|
-
t.isJSXIdentifier(attr.name) &&
|
|
456
|
+
const nameAttr = node.openingElement.attributes.find(attr =>
|
|
457
|
+
t.isJSXAttribute(attr) &&
|
|
458
|
+
t.isJSXIdentifier(attr.name) &&
|
|
243
459
|
attr.name.name === 'name'
|
|
244
460
|
);
|
|
245
|
-
|
|
461
|
+
|
|
246
462
|
if (!nameAttr) {
|
|
247
|
-
debugLog('[mdi-auto-import] No name attribute found');
|
|
463
|
+
debugLog('[mdi-auto-import-enhanced] No name attribute found');
|
|
248
464
|
return;
|
|
249
465
|
}
|
|
250
|
-
|
|
466
|
+
|
|
251
467
|
let iconNames = [];
|
|
252
|
-
|
|
468
|
+
|
|
253
469
|
// Handle both string literals and JSX expressions
|
|
254
470
|
if (nameAttr && t.isStringLiteral(nameAttr.value)) {
|
|
255
|
-
|
|
256
|
-
|
|
471
|
+
const iconName = extractIconName(nameAttr.value.value);
|
|
472
|
+
if (iconName) {
|
|
473
|
+
iconNames = [iconName];
|
|
474
|
+
debugLog(`[mdi-auto-import-enhanced] Found direct string literal: ${iconName}`);
|
|
475
|
+
}
|
|
257
476
|
} else if (nameAttr && t.isJSXExpressionContainer(nameAttr.value)) {
|
|
258
477
|
// Handle JSX expressions with enhanced detection
|
|
259
478
|
const expression = nameAttr.value.expression;
|
|
260
479
|
iconNames = extractIconNames(expression, path);
|
|
261
|
-
|
|
480
|
+
|
|
262
481
|
if (iconNames.length === 0) {
|
|
263
|
-
|
|
264
|
-
console.warn(`[mdi-auto-import] Cannot determine icon name (${nameAttr.value.expression}) for dynamic expression at ${path.node.loc ? `${path.node.loc.start.line}:${path.node.loc.start.column}` : 'unknown location'}. Consider adding icon names to manifest (${manifestPath}) for auto-import support.`);
|
|
482
|
+
debugLog(`[mdi-auto-import-enhanced] Cannot determine icon name for dynamic expression`);
|
|
265
483
|
return;
|
|
266
484
|
}
|
|
267
485
|
}
|
|
268
|
-
|
|
486
|
+
|
|
269
487
|
if (iconNames.length > 0) {
|
|
270
|
-
debugLog(`[mdi-auto-import] Processing icons: ${iconNames.join(', ')}`);
|
|
271
|
-
|
|
488
|
+
debugLog(`[mdi-auto-import-enhanced] Processing icons: ${iconNames.join(', ')}`);
|
|
489
|
+
|
|
272
490
|
// Process each icon name found
|
|
273
491
|
const processedIcons = [];
|
|
274
492
|
iconNames.forEach(iconName => {
|
|
275
493
|
try {
|
|
276
494
|
const mdiIconName = getMdiIconName(iconName);
|
|
277
495
|
const iconIdentifier = getIconIdentifier(mdiIconName);
|
|
278
|
-
|
|
496
|
+
|
|
279
497
|
// Track that we need to import this icon
|
|
280
498
|
importedIcons.add(mdiIconName);
|
|
281
499
|
processedIcons.push({ iconName, mdiIconName, iconIdentifier });
|
|
282
|
-
debugLog(`[mdi-auto-import] Added icon to import list: ${mdiIconName}`);
|
|
500
|
+
debugLog(`[mdi-auto-import-enhanced] Added icon to import list: ${mdiIconName}`);
|
|
283
501
|
} catch (error) {
|
|
284
|
-
console.error(`[mdi-auto-import] Error processing icon "${iconName}": ${error.message}`);
|
|
502
|
+
console.error(`[mdi-auto-import-enhanced] Error processing icon "${iconName}": ${error.message}`);
|
|
285
503
|
}
|
|
286
504
|
});
|
|
287
|
-
|
|
505
|
+
|
|
288
506
|
// If we have exactly one icon, we can transform the component
|
|
289
507
|
if (processedIcons.length === 1) {
|
|
290
508
|
const { iconIdentifier } = processedIcons[0];
|
|
291
|
-
|
|
509
|
+
|
|
292
510
|
// Replace name="iconName" with path={iconIdentifier}
|
|
293
511
|
const pathAttr = t.jsxAttribute(
|
|
294
512
|
t.jsxIdentifier('path'),
|
|
295
513
|
t.jsxExpressionContainer(t.identifier(iconIdentifier))
|
|
296
514
|
);
|
|
297
|
-
|
|
515
|
+
|
|
298
516
|
// Remove the name attribute and add the path attribute
|
|
299
517
|
node.openingElement.attributes = node.openingElement.attributes
|
|
300
518
|
.filter(attr => !(
|
|
301
|
-
t.isJSXAttribute(attr) &&
|
|
302
|
-
t.isJSXIdentifier(attr.name) &&
|
|
519
|
+
t.isJSXAttribute(attr) &&
|
|
520
|
+
t.isJSXIdentifier(attr.name) &&
|
|
303
521
|
attr.name.name === 'name'
|
|
304
522
|
))
|
|
305
523
|
.concat(pathAttr);
|
|
306
|
-
|
|
307
|
-
debugLog(`[mdi-auto-import] Transformed Icon component: name="${processedIcons[0].iconName}" -> path={${iconIdentifier}}`);
|
|
524
|
+
|
|
525
|
+
debugLog(`[mdi-auto-import-enhanced] Transformed Icon component: name="${processedIcons[0].iconName}" -> path={${iconIdentifier}}`);
|
|
308
526
|
} else if (processedIcons.length > 1) {
|
|
309
|
-
|
|
310
|
-
debugLog(`[mdi-auto-import] Found multiple possible icons (${processedIcons.length}), adding imports but not transforming component`);
|
|
311
|
-
console.warn(`[mdi-auto-import] Found conditional icon usage at ${path.node.loc ? `${path.node.loc.start.line}:${path.node.loc.start.column}` : 'unknown location'}. All possible icons will be imported, but the component will not be auto-transformed. Consider manual transformation if needed.`);
|
|
527
|
+
debugLog(`[mdi-auto-import-enhanced] Found multiple possible icons (${processedIcons.length}), adding imports but not transforming component`);
|
|
312
528
|
}
|
|
313
529
|
} else {
|
|
314
|
-
debugLog('[mdi-auto-import] No icon names found');
|
|
530
|
+
debugLog('[mdi-auto-import-enhanced] No icon names found');
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Handle JSX attributes with string values for icon props
|
|
537
|
+
* This handles: <Badge icon="information" />, <Button leftIcon="plus" />
|
|
538
|
+
*/
|
|
539
|
+
JSXAttribute(path) {
|
|
540
|
+
const { node } = path;
|
|
541
|
+
|
|
542
|
+
debugLog(`[mdi-auto-import-enhanced] JSXAttribute visitor called for: ${node.name ? node.name.name || 'unknown' : 'no-name'}`);
|
|
543
|
+
|
|
544
|
+
// Check if this is an icon-related attribute
|
|
545
|
+
if (!t.isJSXIdentifier(node.name)) {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const attrName = node.name.name;
|
|
550
|
+
const iconPropNames = ['name', 'leftIcon', 'rightIcon', 'icon'];
|
|
551
|
+
|
|
552
|
+
if (!iconPropNames.includes(attrName)) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Check if the value is a string literal
|
|
557
|
+
if (!t.isStringLiteral(node.value)) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const stringValue = node.value.value;
|
|
562
|
+
const iconName = extractIconName(stringValue);
|
|
563
|
+
|
|
564
|
+
if (!iconName) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Check if the parent element is a component that supports icons
|
|
569
|
+
const openingElement = path.parentPath.node;
|
|
570
|
+
if (!t.isJSXOpeningElement(openingElement) || !t.isJSXIdentifier(openingElement.name)) {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const componentName = openingElement.name.name;
|
|
575
|
+
const supportedComponents = ['Icon', 'Button', 'Badge', 'Breadcrumb', 'Menu'];
|
|
576
|
+
|
|
577
|
+
if (!supportedComponents.includes(componentName)) {
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
try {
|
|
582
|
+
const mdiIconName = getMdiIconName(iconName);
|
|
583
|
+
importedIcons.add(mdiIconName);
|
|
584
|
+
debugLog(`[mdi-auto-import-enhanced] Added icon from ${componentName}.${attrName}="${stringValue}": ${mdiIconName}`);
|
|
585
|
+
} catch (error) {
|
|
586
|
+
console.error(`[mdi-auto-import-enhanced] Error processing icon "${iconName}" from ${componentName}.${attrName}: ${error.message}`);
|
|
587
|
+
}
|
|
588
|
+
},
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Second pass: Transform string literals that are icon-related
|
|
592
|
+
* This handles cases like: const iconName = "home"; <Icon name={iconName} />
|
|
593
|
+
*/
|
|
594
|
+
StringLiteral(path) {
|
|
595
|
+
// Skip if we're already in a JSX attribute (handled above)
|
|
596
|
+
if (t.isJSXAttribute(path.parent)) {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
const stringValue = path.node.value;
|
|
601
|
+
|
|
602
|
+
// Check if this string is icon-related based on context
|
|
603
|
+
if (isIconRelatedString(path, stringValue)) {
|
|
604
|
+
const iconName = extractIconName(stringValue);
|
|
605
|
+
if (iconName) {
|
|
606
|
+
try {
|
|
607
|
+
const mdiIconName = getMdiIconName(iconName);
|
|
608
|
+
importedIcons.add(mdiIconName);
|
|
609
|
+
debugLog(`[mdi-auto-import-enhanced] Added icon from context-aware string: ${mdiIconName}`);
|
|
610
|
+
} catch (error) {
|
|
611
|
+
console.error(`[mdi-auto-import-enhanced] Error processing icon string "${iconName}": ${error.message}`);
|
|
612
|
+
}
|
|
315
613
|
}
|
|
316
614
|
}
|
|
317
615
|
}
|
|
318
616
|
}
|
|
319
617
|
};
|
|
320
|
-
};
|
|
618
|
+
};
|