rn-iconify 2.1.0 → 2.2.0
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/lib/commonjs/IconRenderer.js +54 -12
- package/lib/commonjs/IconRenderer.js.map +1 -1
- package/lib/commonjs/babel/cache-writer.js +102 -26
- package/lib/commonjs/babel/cache-writer.js.map +1 -1
- package/lib/commonjs/babel/plugin.js +129 -44
- package/lib/commonjs/babel/plugin.js.map +1 -1
- package/lib/commonjs/babel/scanner.js +219 -0
- package/lib/commonjs/babel/scanner.js.map +1 -0
- package/lib/commonjs/babel/types.js.map +1 -1
- package/lib/commonjs/cache/CacheManager.js +82 -1
- package/lib/commonjs/cache/CacheManager.js.map +1 -1
- package/lib/commonjs/cache/DiskCache.js +33 -4
- package/lib/commonjs/cache/DiskCache.js.map +1 -1
- package/lib/commonjs/cli/CLAUDE.md +7 -0
- package/lib/commonjs/cli/commands/bundle.js +37 -6
- package/lib/commonjs/cli/commands/bundle.js.map +1 -1
- package/lib/commonjs/config/ConfigManager.js +8 -1
- package/lib/commonjs/config/ConfigManager.js.map +1 -1
- package/lib/commonjs/config/index.js.map +1 -1
- package/lib/commonjs/config/types.js +9 -0
- package/lib/commonjs/config/types.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/metro/devServerMiddleware.js +150 -0
- package/lib/commonjs/metro/devServerMiddleware.js.map +1 -0
- package/lib/commonjs/metro/index.js +20 -0
- package/lib/commonjs/metro/index.js.map +1 -0
- package/lib/commonjs/metro/types.js +6 -0
- package/lib/commonjs/metro/types.js.map +1 -0
- package/lib/commonjs/metro/withRnIconify.js +53 -0
- package/lib/commonjs/metro/withRnIconify.js.map +1 -0
- package/lib/commonjs/network/IconifyAPI.js +30 -2
- package/lib/commonjs/network/IconifyAPI.js.map +1 -1
- package/lib/module/IconRenderer.js +54 -12
- package/lib/module/IconRenderer.js.map +1 -1
- package/lib/module/babel/cache-writer.js +99 -26
- package/lib/module/babel/cache-writer.js.map +1 -1
- package/lib/module/babel/plugin.js +129 -46
- package/lib/module/babel/plugin.js.map +1 -1
- package/lib/module/babel/scanner.js +213 -0
- package/lib/module/babel/scanner.js.map +1 -0
- package/lib/module/babel/types.js.map +1 -1
- package/lib/module/cache/CacheManager.js +82 -1
- package/lib/module/cache/CacheManager.js.map +1 -1
- package/lib/module/cache/DiskCache.js +32 -4
- package/lib/module/cache/DiskCache.js.map +1 -1
- package/lib/module/cli/CLAUDE.md +7 -0
- package/lib/module/cli/commands/bundle.js +37 -6
- package/lib/module/cli/commands/bundle.js.map +1 -1
- package/lib/module/config/ConfigManager.js +8 -1
- package/lib/module/config/ConfigManager.js.map +1 -1
- package/lib/module/config/index.js.map +1 -1
- package/lib/module/config/types.js +9 -0
- package/lib/module/config/types.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/metro/devServerMiddleware.js +143 -0
- package/lib/module/metro/devServerMiddleware.js.map +1 -0
- package/lib/module/metro/index.js +19 -0
- package/lib/module/metro/index.js.map +1 -0
- package/lib/module/metro/types.js +2 -0
- package/lib/module/metro/types.js.map +1 -0
- package/lib/module/metro/withRnIconify.js +48 -0
- package/lib/module/metro/withRnIconify.js.map +1 -0
- package/lib/module/network/IconifyAPI.js +30 -2
- package/lib/module/network/IconifyAPI.js.map +1 -1
- package/lib/typescript/IconRenderer.d.ts.map +1 -1
- package/lib/typescript/babel/cache-writer.d.ts +16 -2
- package/lib/typescript/babel/cache-writer.d.ts.map +1 -1
- package/lib/typescript/babel/plugin.d.ts +5 -0
- package/lib/typescript/babel/plugin.d.ts.map +1 -1
- package/lib/typescript/babel/scanner.d.ts +31 -0
- package/lib/typescript/babel/scanner.d.ts.map +1 -0
- package/lib/typescript/babel/types.d.ts +8 -1
- package/lib/typescript/babel/types.d.ts.map +1 -1
- package/lib/typescript/cache/CacheManager.d.ts +16 -0
- package/lib/typescript/cache/CacheManager.d.ts.map +1 -1
- package/lib/typescript/cache/DiskCache.d.ts +2 -0
- package/lib/typescript/cache/DiskCache.d.ts.map +1 -1
- package/lib/typescript/cli/commands/bundle.d.ts.map +1 -1
- package/lib/typescript/components/Charm.d.ts +1 -1
- package/lib/typescript/components/Dashicons.d.ts +1 -1
- package/lib/typescript/components/Hugeicons.d.ts +1 -1
- package/lib/typescript/components/Ix.d.ts +1 -1
- package/lib/typescript/components/Tabler.d.ts +1 -1
- package/lib/typescript/components/Token.d.ts +1 -1
- package/lib/typescript/components/TokenBranded.d.ts +1 -1
- package/lib/typescript/config/ConfigManager.d.ts +5 -1
- package/lib/typescript/config/ConfigManager.d.ts.map +1 -1
- package/lib/typescript/config/index.d.ts +1 -1
- package/lib/typescript/config/index.d.ts.map +1 -1
- package/lib/typescript/config/types.d.ts +26 -0
- package/lib/typescript/config/types.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/metro/devServerMiddleware.d.ts +11 -0
- package/lib/typescript/metro/devServerMiddleware.d.ts.map +1 -0
- package/lib/typescript/metro/index.d.ts +19 -0
- package/lib/typescript/metro/index.d.ts.map +1 -0
- package/lib/typescript/metro/types.d.ts +46 -0
- package/lib/typescript/metro/types.d.ts.map +1 -0
- package/lib/typescript/metro/withRnIconify.d.ts +20 -0
- package/lib/typescript/metro/withRnIconify.d.ts.map +1 -0
- package/lib/typescript/network/IconifyAPI.d.ts.map +1 -1
- package/metro.js +12 -0
- package/package.json +13 -6
- package/src/IconRenderer.tsx +59 -12
- package/src/babel/cache-writer.ts +105 -31
- package/src/babel/plugin.ts +157 -34
- package/src/babel/scanner.ts +274 -0
- package/src/babel/types.ts +9 -1
- package/src/cache/CacheManager.ts +83 -1
- package/src/cache/DiskCache.ts +43 -4
- package/src/cli/CLAUDE.md +7 -0
- package/src/cli/commands/bundle.ts +52 -6
- package/src/config/ConfigManager.ts +9 -0
- package/src/config/index.ts +1 -0
- package/src/config/types.ts +35 -0
- package/src/index.ts +1 -0
- package/src/metro/devServerMiddleware.ts +137 -0
- package/src/metro/index.ts +19 -0
- package/src/metro/types.ts +52 -0
- package/src/metro/withRnIconify.ts +52 -0
- package/src/network/IconifyAPI.ts +23 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IconifyAPI.d.ts","sourceRoot":"","sources":["../../../src/network/IconifyAPI.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"IconifyAPI.d.ts","sourceRoot":"","sources":["../../../src/network/IconifyAPI.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA+GH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAcvF;AAsCD;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAqGvF;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EAAE,EACnB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,gBAAgB,CAAC,CA+E3B;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAWvD;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACvC;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,qBAAqB,CAAC,CAoDhC;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,EAAE,EACnB,KAAK,GAAE,MAAY,EACnB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,EAAE,CAAC,CAgCnB"}
|
package/metro.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rn-iconify Metro Plugin
|
|
3
|
+
*
|
|
4
|
+
* Adds dev server middleware for runtime icon usage learning.
|
|
5
|
+
*
|
|
6
|
+
* Usage in metro.config.js:
|
|
7
|
+
* const { withRnIconify } = require('rn-iconify/metro');
|
|
8
|
+
* module.exports = withRnIconify(config);
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Re-export the Metro plugin from the built output
|
|
12
|
+
module.exports = require('./lib/commonjs/metro/index.js');
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rn-iconify",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "268,000+ Iconify icons for React Native with native MMKV caching and full TypeScript autocomplete",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
7
7
|
"types": "lib/typescript/index.d.ts",
|
|
8
8
|
"react-native": "src/index.ts",
|
|
9
9
|
"source": "src/index.ts",
|
|
10
|
+
"sideEffects": false,
|
|
10
11
|
"exports": {
|
|
11
12
|
".": {
|
|
12
13
|
"types": "./lib/typescript/index.d.ts",
|
|
@@ -27,16 +28,19 @@
|
|
|
27
28
|
"types": "./lib/typescript/animated/index.d.ts",
|
|
28
29
|
"import": "./lib/module/animated/index.js",
|
|
29
30
|
"require": "./lib/commonjs/animated/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./metro": {
|
|
33
|
+
"types": "./lib/typescript/metro/index.d.ts",
|
|
34
|
+
"import": "./lib/module/metro/index.js",
|
|
35
|
+
"require": "./lib/commonjs/metro/index.js"
|
|
30
36
|
}
|
|
31
37
|
},
|
|
32
|
-
"bin":
|
|
33
|
-
"rn-iconify": "./lib/commonjs/cli/index.js"
|
|
34
|
-
},
|
|
38
|
+
"bin": "./lib/commonjs/cli/index.js",
|
|
35
39
|
"files": [
|
|
36
40
|
"src",
|
|
37
41
|
"lib",
|
|
38
|
-
"bin",
|
|
39
42
|
"babel.js",
|
|
43
|
+
"metro.js",
|
|
40
44
|
"README.md",
|
|
41
45
|
"assets",
|
|
42
46
|
"!**/__tests__",
|
|
@@ -57,7 +61,7 @@
|
|
|
57
61
|
"prepare": "husky",
|
|
58
62
|
"prepublishOnly": "bob build",
|
|
59
63
|
"generate-components": "tsx scripts/generate-components.ts",
|
|
60
|
-
"release": "
|
|
64
|
+
"semantic-release": "semantic-release"
|
|
61
65
|
},
|
|
62
66
|
"keywords": [
|
|
63
67
|
"react-native",
|
|
@@ -112,6 +116,8 @@
|
|
|
112
116
|
"@commitlint/config-conventional": "^19.0.0",
|
|
113
117
|
"@react-native/babel-preset": "^0.82.1",
|
|
114
118
|
"@react-native/eslint-config": "^0.74.0",
|
|
119
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
120
|
+
"@semantic-release/git": "^10.0.1",
|
|
115
121
|
"@testing-library/react-native": "^12.0.0",
|
|
116
122
|
"@types/jest": "^29.5.0",
|
|
117
123
|
"@types/react": "^18.2.0",
|
|
@@ -129,6 +135,7 @@
|
|
|
129
135
|
"react-native-mmkv": "^3.0.0",
|
|
130
136
|
"react-native-svg": "^15.0.0",
|
|
131
137
|
"react-test-renderer": "^18.2.0",
|
|
138
|
+
"semantic-release": "^25.0.2",
|
|
132
139
|
"tsx": "^4.20.6",
|
|
133
140
|
"typescript": "^5.4.0"
|
|
134
141
|
},
|
package/src/IconRenderer.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import { CacheManager } from './cache/CacheManager';
|
|
|
10
10
|
import { fetchIcon } from './network/IconifyAPI';
|
|
11
11
|
import { PlaceholderFactory } from './placeholder';
|
|
12
12
|
import { useIconAnimation } from './animated/useIconAnimation';
|
|
13
|
+
import { ConfigManager } from './config';
|
|
13
14
|
import type { IconRendererProps, IconLoadingState } from './types';
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -60,13 +61,25 @@ export function IconRenderer({
|
|
|
60
61
|
autoPlay = true,
|
|
61
62
|
onAnimationComplete,
|
|
62
63
|
}: IconRendererProps) {
|
|
64
|
+
// Resolve defaults from config
|
|
65
|
+
const defaultsConfig = ConfigManager.getDefaultsConfig();
|
|
66
|
+
const effectivePlaceholder = placeholder !== undefined
|
|
67
|
+
? placeholder
|
|
68
|
+
: (defaultsConfig.placeholder !== false ? defaultsConfig.placeholder : undefined);
|
|
69
|
+
const fadeIn = defaultsConfig.fadeIn;
|
|
70
|
+
const fadeInDuration = defaultsConfig.fadeInDuration;
|
|
71
|
+
|
|
63
72
|
const [svg, setSvg] = useState<string | null>(null);
|
|
64
73
|
const [state, setState] = useState<IconLoadingState>('idle');
|
|
65
74
|
const [showFallback, setShowFallback] = useState(false);
|
|
75
|
+
const [wasCacheHit, setWasCacheHit] = useState(false);
|
|
66
76
|
const mountedRef = useRef(true);
|
|
67
77
|
const fallbackTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
68
78
|
const abortControllerRef = useRef<AbortController | null>(null);
|
|
69
|
-
const isLoadingRef = useRef(false);
|
|
79
|
+
const isLoadingRef = useRef(false);
|
|
80
|
+
|
|
81
|
+
// Fade-in animation value
|
|
82
|
+
const fadeAnim = useRef(new Animated.Value(0)).current;
|
|
70
83
|
|
|
71
84
|
// Calculate dimensions
|
|
72
85
|
const iconWidth = propWidth ?? size;
|
|
@@ -99,7 +112,9 @@ export function IconRenderer({
|
|
|
99
112
|
if (mountedRef.current) {
|
|
100
113
|
setSvg(cached);
|
|
101
114
|
setState('loaded');
|
|
115
|
+
setWasCacheHit(true);
|
|
102
116
|
isLoadingRef.current = false;
|
|
117
|
+
fadeAnim.setValue(1); // No fade for cache hits
|
|
103
118
|
onLoad?.();
|
|
104
119
|
}
|
|
105
120
|
return;
|
|
@@ -108,15 +123,15 @@ export function IconRenderer({
|
|
|
108
123
|
// 2. Set loading state and start fallback timer
|
|
109
124
|
setState('loading');
|
|
110
125
|
isLoadingRef.current = true;
|
|
126
|
+
setWasCacheHit(false);
|
|
111
127
|
|
|
112
128
|
if (fallbackDelay > 0) {
|
|
113
129
|
fallbackTimerRef.current = setTimeout(() => {
|
|
114
|
-
// Use ref instead of state to avoid stale closure
|
|
115
130
|
if (mountedRef.current && isLoadingRef.current) {
|
|
116
131
|
setShowFallback(true);
|
|
117
132
|
}
|
|
118
133
|
}, fallbackDelay);
|
|
119
|
-
} else if (fallback) {
|
|
134
|
+
} else if (fallback || effectivePlaceholder !== undefined) {
|
|
120
135
|
setShowFallback(true);
|
|
121
136
|
}
|
|
122
137
|
|
|
@@ -125,17 +140,28 @@ export function IconRenderer({
|
|
|
125
140
|
const fetchedSvg = await fetchIcon(iconName, abortControllerRef.current.signal);
|
|
126
141
|
|
|
127
142
|
if (mountedRef.current) {
|
|
128
|
-
// Store in cache
|
|
129
143
|
CacheManager.set(iconName, fetchedSvg);
|
|
130
144
|
|
|
131
145
|
setSvg(fetchedSvg);
|
|
132
146
|
setState('loaded');
|
|
133
147
|
isLoadingRef.current = false;
|
|
134
148
|
setShowFallback(false);
|
|
149
|
+
|
|
150
|
+
// Fade-in for non-cached icons
|
|
151
|
+
if (fadeIn) {
|
|
152
|
+
fadeAnim.setValue(0);
|
|
153
|
+
Animated.timing(fadeAnim, {
|
|
154
|
+
toValue: 1,
|
|
155
|
+
duration: fadeInDuration,
|
|
156
|
+
useNativeDriver: true,
|
|
157
|
+
}).start();
|
|
158
|
+
} else {
|
|
159
|
+
fadeAnim.setValue(1);
|
|
160
|
+
}
|
|
161
|
+
|
|
135
162
|
onLoad?.();
|
|
136
163
|
}
|
|
137
164
|
} catch (error) {
|
|
138
|
-
// Ignore abort errors
|
|
139
165
|
if (error instanceof Error && error.name === 'AbortError') {
|
|
140
166
|
return;
|
|
141
167
|
}
|
|
@@ -145,7 +171,7 @@ export function IconRenderer({
|
|
|
145
171
|
onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
146
172
|
}
|
|
147
173
|
}
|
|
148
|
-
}, [iconName, fallback, fallbackDelay, onLoad, onError
|
|
174
|
+
}, [iconName, fallback, fallbackDelay, onLoad, onError, effectivePlaceholder, fadeIn, fadeInDuration, fadeAnim]);
|
|
149
175
|
|
|
150
176
|
// Effect to load icon
|
|
151
177
|
useEffect(() => {
|
|
@@ -194,6 +220,9 @@ export function IconRenderer({
|
|
|
194
220
|
// Determine if we should show placeholder/fallback
|
|
195
221
|
const shouldShowPlaceholder = (state === 'loading' && showFallback) || state === 'error';
|
|
196
222
|
|
|
223
|
+
// Whether to use fade-in wrapper (only for non-cached, non-animated icons)
|
|
224
|
+
const useFadeIn = fadeIn && !wasCacheHit && state === 'loaded' && !hasAnimation;
|
|
225
|
+
|
|
197
226
|
// Check if icon should be pressable
|
|
198
227
|
const isPressable = !!(onPress || onLongPress);
|
|
199
228
|
|
|
@@ -224,8 +253,8 @@ export function IconRenderer({
|
|
|
224
253
|
|
|
225
254
|
// Render placeholder or fallback during loading/error
|
|
226
255
|
if (shouldShowPlaceholder) {
|
|
227
|
-
// Priority:
|
|
228
|
-
if (
|
|
256
|
+
// Priority: effectivePlaceholder (includes config default) > fallback
|
|
257
|
+
if (effectivePlaceholder !== undefined) {
|
|
229
258
|
return wrapWithPressable(
|
|
230
259
|
<View
|
|
231
260
|
style={[{ width: iconWidth, height: iconHeight }, style]}
|
|
@@ -234,7 +263,7 @@ export function IconRenderer({
|
|
|
234
263
|
{...nativeWindProps}
|
|
235
264
|
>
|
|
236
265
|
<PlaceholderFactory
|
|
237
|
-
type={
|
|
266
|
+
type={effectivePlaceholder}
|
|
238
267
|
width={iconWidth}
|
|
239
268
|
height={iconHeight}
|
|
240
269
|
color={placeholderColor}
|
|
@@ -291,7 +320,25 @@ export function IconRenderer({
|
|
|
291
320
|
);
|
|
292
321
|
}
|
|
293
322
|
|
|
294
|
-
// Render without animation
|
|
323
|
+
// Render without animation (with optional fade-in)
|
|
324
|
+
if (useFadeIn) {
|
|
325
|
+
return wrapWithPressable(
|
|
326
|
+
<Animated.View
|
|
327
|
+
style={[
|
|
328
|
+
styles.container,
|
|
329
|
+
{ width: iconWidth, height: iconHeight, transform: transformStyle, opacity: fadeAnim },
|
|
330
|
+
style,
|
|
331
|
+
]}
|
|
332
|
+
accessibilityLabel={accessibilityLabel}
|
|
333
|
+
accessibilityRole="image"
|
|
334
|
+
testID={testID}
|
|
335
|
+
{...nativeWindProps}
|
|
336
|
+
>
|
|
337
|
+
<SvgXml xml={colorizedSvg} width={iconWidth} height={iconHeight} />
|
|
338
|
+
</Animated.View>
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
295
342
|
return wrapWithPressable(
|
|
296
343
|
<View
|
|
297
344
|
style={[
|
|
@@ -310,7 +357,7 @@ export function IconRenderer({
|
|
|
310
357
|
}
|
|
311
358
|
|
|
312
359
|
// Show placeholder immediately if set (no delay), otherwise empty view
|
|
313
|
-
if (
|
|
360
|
+
if (effectivePlaceholder !== undefined && state === 'loading') {
|
|
314
361
|
return wrapWithPressable(
|
|
315
362
|
<View
|
|
316
363
|
style={[{ width: iconWidth, height: iconHeight }, style]}
|
|
@@ -319,7 +366,7 @@ export function IconRenderer({
|
|
|
319
366
|
{...nativeWindProps}
|
|
320
367
|
>
|
|
321
368
|
<PlaceholderFactory
|
|
322
|
-
type={
|
|
369
|
+
type={effectivePlaceholder}
|
|
323
370
|
width={iconWidth}
|
|
324
371
|
height={iconHeight}
|
|
325
372
|
color={placeholderColor}
|
|
@@ -233,7 +233,33 @@ export async function fetchAndCreateBundle(
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
/**
|
|
236
|
-
*
|
|
236
|
+
* Read existing bundle from disk
|
|
237
|
+
* Returns null if file doesn't exist or is invalid
|
|
238
|
+
*/
|
|
239
|
+
export function readExistingBundle(bundlePath: string): IconBundle | null {
|
|
240
|
+
try {
|
|
241
|
+
if (!fs.existsSync(bundlePath)) return null;
|
|
242
|
+
const content = fs.readFileSync(bundlePath, 'utf-8');
|
|
243
|
+
const bundle = JSON.parse(content) as IconBundle;
|
|
244
|
+
if (bundle.version === '1.0.0' && typeof bundle.count === 'number' && bundle.icons) {
|
|
245
|
+
return bundle;
|
|
246
|
+
}
|
|
247
|
+
} catch {
|
|
248
|
+
// Invalid bundle file
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Determine which icons are new and need to be fetched
|
|
255
|
+
*/
|
|
256
|
+
export function getNewIconNames(allIcons: string[], existingBundle: IconBundle | null): string[] {
|
|
257
|
+
if (!existingBundle) return allIcons;
|
|
258
|
+
return allIcons.filter((name) => !existingBundle.icons[name]);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Write bundle to file (only if content changed)
|
|
237
263
|
*/
|
|
238
264
|
export function writeBundleToFile(bundle: IconBundle, outputPath: string, verbose: boolean): void {
|
|
239
265
|
// Ensure directory exists
|
|
@@ -242,8 +268,29 @@ export function writeBundleToFile(bundle: IconBundle, outputPath: string, verbos
|
|
|
242
268
|
fs.mkdirSync(dir, { recursive: true });
|
|
243
269
|
}
|
|
244
270
|
|
|
245
|
-
// Write bundle
|
|
271
|
+
// Write bundle JSON
|
|
246
272
|
const content = JSON.stringify(bundle);
|
|
273
|
+
|
|
274
|
+
// Check if content actually changed
|
|
275
|
+
if (fs.existsSync(outputPath)) {
|
|
276
|
+
try {
|
|
277
|
+
const existing = fs.readFileSync(outputPath, 'utf-8');
|
|
278
|
+
const existingBundle = JSON.parse(existing) as IconBundle;
|
|
279
|
+
if (existingBundle.count === bundle.count && existingBundle.version === bundle.version) {
|
|
280
|
+
const existingKeys = Object.keys(existingBundle.icons).sort().join(',');
|
|
281
|
+
const newKeys = Object.keys(bundle.icons).sort().join(',');
|
|
282
|
+
if (existingKeys === newKeys) {
|
|
283
|
+
if (verbose) {
|
|
284
|
+
console.log('[rn-iconify] Bundle unchanged, skipping write');
|
|
285
|
+
}
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
} catch {
|
|
290
|
+
// Existing file is corrupted, proceed with write
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
247
294
|
fs.writeFileSync(outputPath, content, 'utf-8');
|
|
248
295
|
|
|
249
296
|
if (verbose) {
|
|
@@ -252,15 +299,26 @@ export function writeBundleToFile(bundle: IconBundle, outputPath: string, verbos
|
|
|
252
299
|
}
|
|
253
300
|
}
|
|
254
301
|
|
|
302
|
+
/**
|
|
303
|
+
* Resolve the bundle directory path
|
|
304
|
+
*/
|
|
305
|
+
export function resolveBundleDir(outputPath: string, projectRoot: string): string {
|
|
306
|
+
return path.isAbsolute(outputPath)
|
|
307
|
+
? outputPath
|
|
308
|
+
: path.join(projectRoot, outputPath);
|
|
309
|
+
}
|
|
310
|
+
|
|
255
311
|
/**
|
|
256
312
|
* Generate icon bundle from collected icons
|
|
313
|
+
* Supports incremental fetching: only fetches icons not already in the existing bundle
|
|
257
314
|
*/
|
|
258
315
|
export async function generateBundle(
|
|
259
316
|
iconNames: string[],
|
|
260
317
|
options: BabelPluginOptions,
|
|
261
|
-
projectRoot: string
|
|
318
|
+
projectRoot: string,
|
|
319
|
+
existingBundle?: IconBundle | null
|
|
262
320
|
): Promise<void> {
|
|
263
|
-
const { outputPath = '
|
|
321
|
+
const { outputPath = '.rn-iconify', verbose = false } = options;
|
|
264
322
|
|
|
265
323
|
if (iconNames.length === 0) {
|
|
266
324
|
if (verbose) {
|
|
@@ -269,28 +327,55 @@ export async function generateBundle(
|
|
|
269
327
|
return;
|
|
270
328
|
}
|
|
271
329
|
|
|
330
|
+
const bundleDir = resolveBundleDir(outputPath, projectRoot);
|
|
331
|
+
const bundleFile = path.join(bundleDir, 'icons.json');
|
|
332
|
+
|
|
272
333
|
try {
|
|
273
|
-
//
|
|
274
|
-
const
|
|
334
|
+
// Read existing bundle if not provided
|
|
335
|
+
const existing = existingBundle !== undefined ? existingBundle : readExistingBundle(bundleFile);
|
|
336
|
+
|
|
337
|
+
// Determine which icons need fetching
|
|
338
|
+
const newIconNames = getNewIconNames(iconNames, existing);
|
|
275
339
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
340
|
+
if (newIconNames.length === 0 && existing) {
|
|
341
|
+
if (verbose) {
|
|
342
|
+
console.log('[rn-iconify] All icons already bundled, skipping fetch');
|
|
343
|
+
}
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Fetch only new icons
|
|
348
|
+
const newBundle = newIconNames.length > 0
|
|
349
|
+
? await fetchAndCreateBundle(newIconNames, options)
|
|
350
|
+
: { version: '1.0.0', generatedAt: new Date().toISOString(), icons: {}, count: 0 } as IconBundle;
|
|
351
|
+
|
|
352
|
+
// Merge with existing bundle
|
|
353
|
+
const mergedIcons = {
|
|
354
|
+
...(existing?.icons || {}),
|
|
355
|
+
...newBundle.icons,
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const bundle: IconBundle = {
|
|
359
|
+
version: '1.0.0',
|
|
360
|
+
generatedAt: new Date().toISOString(),
|
|
361
|
+
icons: mergedIcons,
|
|
362
|
+
count: Object.keys(mergedIcons).length,
|
|
363
|
+
};
|
|
280
364
|
|
|
281
365
|
// Write bundle to file
|
|
282
366
|
writeBundleToFile(bundle, bundleFile, verbose);
|
|
283
367
|
|
|
284
|
-
// Also write a JS module for
|
|
285
|
-
const jsContent = `// Auto-generated by rn-iconify babel plugin
|
|
286
|
-
|
|
287
|
-
module.exports = ${JSON.stringify(bundle)};
|
|
288
|
-
`;
|
|
289
|
-
const jsFile = bundleFile.replace('.json', '.js');
|
|
368
|
+
// Also write a JS module for direct require() by Metro
|
|
369
|
+
const jsContent = `// Auto-generated by rn-iconify babel plugin\n// Do not edit manually\nmodule.exports = ${JSON.stringify(bundle)};\n`;
|
|
370
|
+
const jsFile = path.join(bundleDir, 'icons.js');
|
|
290
371
|
fs.writeFileSync(jsFile, jsContent, 'utf-8');
|
|
291
372
|
|
|
292
373
|
if (verbose) {
|
|
293
|
-
|
|
374
|
+
const newCount = newIconNames.length;
|
|
375
|
+
const existingCount = existing ? Object.keys(existing.icons).length : 0;
|
|
376
|
+
console.log(
|
|
377
|
+
`[rn-iconify] Bundle generation complete! ${newCount} new + ${existingCount} existing = ${bundle.count} total icons`
|
|
378
|
+
);
|
|
294
379
|
}
|
|
295
380
|
} catch (error) {
|
|
296
381
|
// Don't fail the build on bundle generation error
|
|
@@ -305,19 +390,8 @@ module.exports = ${JSON.stringify(bundle)};
|
|
|
305
390
|
* Check if bundle file exists and is valid
|
|
306
391
|
*/
|
|
307
392
|
export function isBundleValid(outputPath: string, projectRoot: string): boolean {
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
: path.join(projectRoot, outputPath, 'icons.json');
|
|
311
|
-
|
|
312
|
-
if (!fs.existsSync(bundleFile)) {
|
|
313
|
-
return false;
|
|
314
|
-
}
|
|
393
|
+
const bundleDir = resolveBundleDir(outputPath, projectRoot);
|
|
394
|
+
const bundleFile = path.join(bundleDir, 'icons.json');
|
|
315
395
|
|
|
316
|
-
|
|
317
|
-
const content = fs.readFileSync(bundleFile, 'utf-8');
|
|
318
|
-
const bundle = JSON.parse(content) as IconBundle;
|
|
319
|
-
return bundle.version === '1.0.0' && typeof bundle.count === 'number';
|
|
320
|
-
} catch {
|
|
321
|
-
return false;
|
|
322
|
-
}
|
|
396
|
+
return readExistingBundle(bundleFile) !== null;
|
|
323
397
|
}
|