react-native-nano-icons 0.1.7 → 0.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/README.md +75 -13
- package/android/src/main/java/com/nanoicons/NanoIconsFontLoaderModule.kt +58 -0
- package/android/src/main/java/com/nanoicons/NanoIconsPackage.kt +19 -2
- package/ios/NanoIconView.h +5 -0
- package/ios/NanoIconView.mm +35 -9
- package/ios/NanoIconsFontLoader.h +11 -0
- package/ios/NanoIconsFontLoader.mm +110 -0
- package/lib/commonjs/cli/build.d.ts +3 -0
- package/lib/commonjs/cli/build.js +8 -4
- package/lib/commonjs/cli/config.d.ts +1 -0
- package/lib/commonjs/cli/config.js +10 -0
- package/lib/commonjs/cli/expoConfig.d.ts +8 -0
- package/lib/commonjs/cli/expoConfig.js +34 -0
- package/lib/commonjs/cli/index.d.ts +2 -1
- package/lib/commonjs/cli/index.js +4 -1
- package/lib/commonjs/cli/link.js +32 -22
- package/lib/commonjs/plugin/src/types.d.ts +9 -0
- package/lib/commonjs/plugin/src/withNanoIconsFontLinking.d.ts +5 -0
- package/lib/commonjs/plugin/src/withNanoIconsFontLinking.js +24 -10
- package/lib/commonjs/scripts/cli.js +23 -11
- package/lib/commonjs/src/core/pipeline/config.d.ts +1 -0
- package/lib/commonjs/src/core/pipeline/managers.js +1 -2
- package/lib/commonjs/src/core/pipeline/run.js +1 -0
- package/lib/commonjs/src/core/svg/svg_dom.d.ts +1 -0
- package/lib/commonjs/src/core/svg/svg_dom.js +22 -17
- package/lib/commonjs/src/core/svg/svg_pathops.js +30 -16
- package/lib/commonjs/src/core/types.d.ts +3 -0
- package/lib/module/const/codegenPrimitives.js +2 -0
- package/lib/module/const/codegenPrimitives.js.map +1 -0
- package/lib/module/core/font/compile.js.map +1 -1
- package/lib/module/core/pipeline/config.js.map +1 -1
- package/lib/module/core/pipeline/managers.js.map +1 -1
- package/lib/module/core/pipeline/run.js +4 -1
- package/lib/module/core/pipeline/run.js.map +1 -1
- package/lib/module/core/svg/layers.js.map +1 -1
- package/lib/module/core/svg/svg_dom.js +19 -19
- package/lib/module/core/svg/svg_dom.js.map +1 -1
- package/lib/module/core/svg/svg_pathops.js.map +1 -1
- package/lib/module/createNanoIconsSet.js +2 -2
- package/lib/module/createNanoIconsSet.js.map +1 -1
- package/lib/module/createNanoIconsSet.native.js +43 -16
- package/lib/module/createNanoIconsSet.native.js.map +1 -1
- package/lib/module/createNanoIconsSet.shared.js +49 -20
- package/lib/module/createNanoIconsSet.shared.js.map +1 -1
- package/lib/module/createNanoIconsSet.web.js +78 -0
- package/lib/module/createNanoIconsSet.web.js.map +1 -0
- package/lib/module/loadDynamicFont.js +118 -0
- package/lib/module/loadDynamicFont.js.map +1 -0
- package/lib/module/specs/NanoIconViewNativeComponent.ts +9 -8
- package/lib/module/specs/NativeNanoIconsFontLoader.js +7 -0
- package/lib/module/specs/NativeNanoIconsFontLoader.js.map +1 -0
- package/lib/module/utils/glyphRuntime.js +37 -0
- package/lib/module/utils/glyphRuntime.js.map +1 -0
- package/lib/typescript/__tests__/glyphRuntime.unit.test.d.ts +2 -0
- package/lib/typescript/__tests__/glyphRuntime.unit.test.d.ts.map +1 -0
- package/lib/typescript/__tests__/link.unit.test.d.ts +3 -0
- package/lib/typescript/__tests__/link.unit.test.d.ts.map +1 -0
- package/lib/typescript/__tests__/loadDynamicFont.unit.test.d.ts +2 -0
- package/lib/typescript/__tests__/loadDynamicFont.unit.test.d.ts.map +1 -0
- package/lib/typescript/cli/build.d.ts +3 -0
- package/lib/typescript/cli/build.d.ts.map +1 -1
- package/lib/typescript/cli/link.d.ts +12 -0
- package/lib/typescript/cli/link.d.ts.map +1 -0
- package/lib/typescript/src/const/codegenPrimitives.d.ts +3 -0
- package/lib/typescript/src/const/codegenPrimitives.d.ts.map +1 -0
- package/lib/typescript/src/core/font/compile.d.ts.map +1 -1
- package/lib/typescript/src/core/pipeline/config.d.ts +1 -0
- package/lib/typescript/src/core/pipeline/config.d.ts.map +1 -1
- package/lib/typescript/src/core/pipeline/managers.d.ts.map +1 -1
- package/lib/typescript/src/core/pipeline/run.d.ts.map +1 -1
- package/lib/typescript/src/core/svg/svg_dom.d.ts +1 -0
- package/lib/typescript/src/core/svg/svg_dom.d.ts.map +1 -1
- package/lib/typescript/src/core/svg/svg_pathops.d.ts.map +1 -1
- package/lib/typescript/src/core/types.d.ts +3 -0
- package/lib/typescript/src/core/types.d.ts.map +1 -1
- package/lib/typescript/src/createNanoIconsSet.d.ts +1 -0
- package/lib/typescript/src/createNanoIconsSet.d.ts.map +1 -1
- package/lib/typescript/src/createNanoIconsSet.native.d.ts +1 -0
- package/lib/typescript/src/createNanoIconsSet.native.d.ts.map +1 -1
- package/lib/typescript/src/createNanoIconsSet.shared.d.ts +11 -0
- package/lib/typescript/src/createNanoIconsSet.shared.d.ts.map +1 -1
- package/lib/typescript/src/createNanoIconsSet.web.d.ts +7 -0
- package/lib/typescript/src/createNanoIconsSet.web.d.ts.map +1 -0
- package/lib/typescript/src/loadDynamicFont.d.ts +28 -0
- package/lib/typescript/src/loadDynamicFont.d.ts.map +1 -0
- package/lib/typescript/src/specs/NanoIconViewNativeComponent.d.ts +9 -8
- package/lib/typescript/src/specs/NanoIconViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/specs/NativeNanoIconsFontLoader.d.ts +8 -0
- package/lib/typescript/src/specs/NativeNanoIconsFontLoader.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +7 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/glyphRuntime.d.ts +18 -0
- package/lib/typescript/src/utils/glyphRuntime.d.ts.map +1 -0
- package/package.json +8 -5
- package/plugin/src/types.ts +9 -0
- package/plugin/src/withNanoIconsFontLinking.ts +29 -10
- package/react-native-nano-icons.podspec +1 -1
- package/scripts/cli.ts +31 -11
- package/src/const/codegenPrimitives.ts +14 -0
- package/src/core/font/compile.ts +1 -2
- package/src/core/pipeline/config.ts +1 -0
- package/src/core/pipeline/managers.ts +5 -10
- package/src/core/pipeline/run.ts +11 -6
- package/src/core/svg/layers.ts +4 -4
- package/src/core/svg/svg_dom.ts +26 -23
- package/src/core/svg/svg_pathops.ts +50 -24
- package/src/core/types.ts +10 -2
- package/src/createNanoIconsSet.native.tsx +78 -26
- package/src/createNanoIconsSet.shared.tsx +93 -40
- package/src/createNanoIconsSet.tsx +9 -1
- package/src/createNanoIconsSet.web.tsx +109 -0
- package/src/loadDynamicFont.ts +162 -0
- package/src/specs/NanoIconViewNativeComponent.ts +9 -8
- package/src/specs/NativeNanoIconsFontLoader.ts +11 -0
- package/src/types.ts +5 -1
- package/src/utils/glyphRuntime.ts +46 -0
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
<picture>
|
|
4
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/
|
|
5
|
-
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/
|
|
6
|
-
<img alt="Nano Icons" src="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/packages/react-native-nano-icons/docs/img/logo-nanoicons-inverted.svg">
|
|
5
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/packages/react-native-nano-icons/docs/img/logo-nanoicons-default.svg">
|
|
6
|
+
<img alt="Nano Icons" src="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/packages/react-native-nano-icons/docs/img/logo-nanoicons-default.svg">
|
|
7
7
|
</picture>
|
|
8
8
|
|
|
9
9
|
</div>
|
|
@@ -28,9 +28,9 @@ Drop your SVGs in a folder. Use them as a fully typed component by name.
|
|
|
28
28
|
That’s it 🔬⚡️
|
|
29
29
|
|
|
30
30
|
<picture>
|
|
31
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/
|
|
32
|
-
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/
|
|
33
|
-
<img alt="Nano Icons Platforms Showcase" src="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/
|
|
31
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/packages/react-native-nano-icons/docs/img/nano-icons-platforms-inverted.png">
|
|
32
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/packages/react-native-nano-icons/docs/img/nano-icons-platforms-light.png">
|
|
33
|
+
<img alt="Nano Icons Platforms Showcase" src="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/packages/react-native-nano-icons/docs/img/nano-icons-platforms-light.png">
|
|
34
34
|
</picture>
|
|
35
35
|
|
|
36
36
|
---
|
|
@@ -42,8 +42,9 @@ That’s it 🔬⚡️
|
|
|
42
42
|
- [🎨 Multicolor Icons](#-multicolor-icons)
|
|
43
43
|
- [📊 Performance](#-performance)
|
|
44
44
|
- [⚠️ Known Limitations](#%EF%B8%8F-known-limitations)
|
|
45
|
-
- [🔧 Font Generation Pipeline](#-
|
|
45
|
+
- [🔧 Font Generation Pipeline](#-font-generation-pipeline)
|
|
46
46
|
- [🤝 Contributing](#-contributing)
|
|
47
|
+
- [License](#license)
|
|
47
48
|
|
|
48
49
|
---
|
|
49
50
|
|
|
@@ -54,6 +55,7 @@ That’s it 🔬⚡️
|
|
|
54
55
|
- [x] Android API 24+
|
|
55
56
|
- [x] Web
|
|
56
57
|
- [x] Expo Go
|
|
58
|
+
- [x] tvOS 15.1+
|
|
57
59
|
|
|
58
60
|
---
|
|
59
61
|
|
|
@@ -118,6 +120,7 @@ The plugin accepts an object with an `iconSets` array, allowing you to generate
|
|
|
118
120
|
| `outputDir` | `string` | No | `../nanoicons` | Path where the `.ttf` and `.json` artifacts will be saved. Defaults to a sibling `nanoicons` folder relative to the input. |
|
|
119
121
|
| `upm` | `number` | No | `1024` | Units Per Em. Defines the resolution of the font grid. |
|
|
120
122
|
| `startUnicode` | `string` | No | `0xe900` | The starting Hex Unicode point for the first icon glyph. |
|
|
123
|
+
| `linking` | `'static' \| 'dynamic'` | No | `'static'` | Delivery mode for the generated TTF. `'static'` bundles it into the native app. `'dynamic'` excludes it from native linking so the host app can deliver it at runtime (i.e. via OTA update). See [Dynamic linking](#dynamic-linking-expo-ota-updates-support). |
|
|
121
124
|
|
|
122
125
|
<details>
|
|
123
126
|
<summary>Default Dir Path Behavior</summary>
|
|
@@ -203,10 +206,68 @@ export default function App() {
|
|
|
203
206
|
| `testID` | `string` | — | Test identifier for e2e testing frameworks. |
|
|
204
207
|
| `ref` | `Ref<View>` | — | Ref to the underlying native view. |
|
|
205
208
|
|
|
209
|
+
### Dynamic linking (Expo OTA updates support)
|
|
210
|
+
TL;DR the default static linking is best for most use cases as it does not affect JS bundle size at all, but if you have an [OTA updates workflow](https://expo.dev/solutions/eas-ota-updates) and make changes to your icons frequently, you can opt out of native bundling and register a particular iconSet font at runtime explicitly.
|
|
211
|
+
|
|
212
|
+
By default, generated TTFs are bundled into the native app at build/link time. Set `linking: 'dynamic'` to opt out: the build still produces the `.ttf` and `.glyphmap.json`, but then OTA workflows will ship icon outside the native binary - you deliver the file and the library will register it at runtime.
|
|
213
|
+
|
|
214
|
+
**Config**
|
|
215
|
+
|
|
216
|
+
```JSON
|
|
217
|
+
{
|
|
218
|
+
"iconSets": [
|
|
219
|
+
{
|
|
220
|
+
"inputDir": "./assets/icons/dynamic-ota-icons",
|
|
221
|
+
"linking": "dynamic"
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
> [!NOTE]
|
|
228
|
+
> You can mix both linking modes in the same config — some icon sets can be statically bundled and others delivered dynamically. Each entry in `iconSets` is independent.
|
|
229
|
+
|
|
230
|
+
**Runtime**
|
|
231
|
+
|
|
232
|
+
Pass the font as the second argument to `createNanoIconSet`. The library registers it under the family name at runtime.
|
|
233
|
+
|
|
234
|
+
```TypeScript
|
|
235
|
+
import { createNanoIconSet } from "react-native-nano-icons";
|
|
236
|
+
import glyphMap from "./dynamic-ota-icons.glyphmap.json";
|
|
237
|
+
|
|
238
|
+
export const Icon = createNanoIconSet(glyphMap, require("./dynamic-ota-icons.ttf"));
|
|
239
|
+
// or: createNanoIconSet(glyphMap, { uri: "https://cdn.example.com/remote-nano-icons.ttf" })
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
> [!NOTE]
|
|
243
|
+
> In [Expo Go](https://expo.dev/go), the native font loader is unavailable, but you can still see real icons by loading the font manually via [`expo-font`](https://docs.expo.dev/versions/latest/sdk/font/) — use the value of `glyphMap.m.f` as the family name key. [Once you move to a development build](https://docs.expo.dev/develop/development-builds/expo-go-to-dev-build/), the library registers the font automatically and you can remove the `expo-font` setup.
|
|
244
|
+
|
|
245
|
+
> If a dynamic glyphmap gets no font, icons render as tofu until one is registered (with a dev warning).
|
|
246
|
+
|
|
206
247
|
### 5. Font Regeneration
|
|
207
248
|
|
|
208
249
|
**The build script detects changes in path and contents of the SVGs** in your input directory based on a fingerprint hash. If anything changes (file names, SVG attributes/nodes) or the output font/glyphmap files are deleted, the icon set is regenerated during `prebuild` or manual script run.
|
|
209
250
|
|
|
251
|
+
### Regenerating dynamic fonts only (useful for an OTA update) ☁️
|
|
252
|
+
|
|
253
|
+
When your `dynamic` icons change and you want to ship them via OTA update, you don't need to run a full `expo prebuild` and native rebuild. Use `--dynamic` to regenerate only the dynamic sets:
|
|
254
|
+
|
|
255
|
+
```sh
|
|
256
|
+
# run from your app root
|
|
257
|
+
npx react-native-nano-icons --path path/to/.nanoicons.json --dynamic
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
> [!TIP]
|
|
262
|
+
> Using Expo CNG with the app config and expo plugin instead of `.nanoicons.json` ? Just add `--app-config` flag to use your plugin input setup instead:
|
|
263
|
+
> ```sh
|
|
264
|
+
> # run from your app root
|
|
265
|
+
> npx react-native-nano-icons --dynamic --app-config
|
|
266
|
+
> ```
|
|
267
|
+
> This reads your config directly from `app.json` / `app.config.js` / `app.config.ts` (no separate `.nanoicons.json` needed).
|
|
268
|
+
|
|
269
|
+
The CLI rebuilds only the sets defined with `linking: "dynamic"`, and skips all native linking. Commit the updated `.ttf` and `.glyphmap.json` and push your OTA update as usual ☁️ 🚀
|
|
270
|
+
|
|
210
271
|
---
|
|
211
272
|
|
|
212
273
|
## 🎨 Multicolor Icons
|
|
@@ -228,7 +289,7 @@ This makes the library well-suited for multicolor icons like country flags, bran
|
|
|
228
289
|
```
|
|
229
290
|
|
|
230
291
|
<div align="center">
|
|
231
|
-
<img alt="Multicolor icon example showing per-layer color overrides" src="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/
|
|
292
|
+
<img alt="Multicolor icon example showing per-layer color overrides" src="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/packages/react-native-nano-icons/docs/img/nano-icon-color-showcase.png" height="250">
|
|
232
293
|
</div>
|
|
233
294
|
<br>
|
|
234
295
|
|
|
@@ -264,7 +325,7 @@ We measured the average time to render a screen with identical set of 1,000 diff
|
|
|
264
325
|
> These libraries serve different primary purposes. `expo-image` is the go-to image library for photos and remote assets. `@expo/vector-icons` ships with many popular icon sets built in. `react-native-svg` handles the full SVG specification with fine-grained attribute control. We compare them here specifically on the use case of rendering many small, static icons.
|
|
265
326
|
|
|
266
327
|
<div align="center">
|
|
267
|
-
<img alt="Performance benchmark: time to render 1k icons" src="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/
|
|
328
|
+
<img alt="Performance benchmark: time to render 1k icons" src="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/packages/react-native-nano-icons/docs/img/nano-icons-benchmarks.png" width="900">
|
|
268
329
|
</div>
|
|
269
330
|
<br>
|
|
270
331
|
|
|
@@ -278,6 +339,7 @@ The chart shows time in milliseconds across three phases: **JS Thread** (JavaScr
|
|
|
278
339
|
## ⚠️ Known Limitations
|
|
279
340
|
|
|
280
341
|
- SVG `<filter>` and `<mask>` elements are not supported — font glyphs cannot represent these effects.
|
|
342
|
+
- Embedded raster images (`<image>` elements, e.g. base64-encoded bitmaps inside an SVG) are not supported — only vector geometry can be converted to glyphs.
|
|
281
343
|
- Only `*.svg` input files are supported.
|
|
282
344
|
|
|
283
345
|
---
|
|
@@ -285,9 +347,9 @@ The chart shows time in milliseconds across three phases: **JS Thread** (JavaScr
|
|
|
285
347
|
## 🔧 Font Generation Pipeline
|
|
286
348
|
|
|
287
349
|
<picture>
|
|
288
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/
|
|
289
|
-
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/
|
|
290
|
-
<img alt="Nano Icons Pipeline" src="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/
|
|
350
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/packages/react-native-nano-icons/docs/img/nano-icons-graph-inverted.png">
|
|
351
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/packages/react-native-nano-icons/docs/img/nano-icons-graph-light.png">
|
|
352
|
+
<img alt="Nano Icons Pipeline" src="https://raw.githubusercontent.com/software-mansion-labs/react-native-nano-icons/main/packages/react-native-nano-icons/docs/img/nano-icons-graph-light.png">
|
|
291
353
|
</picture>
|
|
292
354
|
|
|
293
355
|
At build time, the pipeline processes your SVG directory through four stages:
|
|
@@ -298,7 +360,7 @@ At build time, the pipeline processes your SVG directory through four stages:
|
|
|
298
360
|
4. **Font compilation** — Layers are compiled into a standard `.ttf` font file, with each layer mapped to a private-use Unicode codepoint.
|
|
299
361
|
5. **Glyphmap generation** — A compact `.glyphmap.json` is created, mapping icon names to their codepoints, default colors, and metrics.
|
|
300
362
|
|
|
301
|
-
At runtime, the native component stacks glyph layers at the same position — one `drawGlyphs` call per layer via [CoreText](https://developer.apple.com/documentation/coretext/) (iOS) or `drawText` via [Canvas](<https://developer.android.com/reference/android/graphics/Canvas#drawText(java.lang.String,%20float,%20float,%20android.graphics.Paint)>) (Android). On web
|
|
363
|
+
At runtime, the native component stacks glyph layers at the same position — one `drawGlyphs` call per layer via [CoreText](https://developer.apple.com/documentation/coretext/) (iOS) or `drawText` via [Canvas](<https://developer.android.com/reference/android/graphics/Canvas#drawText(java.lang.String,%20float,%20float,%20android.graphics.Paint)>) (Android). On the web, icons render as stacked inline `<span>` elements. In Expo Go, a pure `react-native` fallback uses stacked `<Text>` elements.
|
|
302
364
|
|
|
303
365
|
---
|
|
304
366
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
package com.nanoicons
|
|
2
|
+
|
|
3
|
+
import android.graphics.Typeface
|
|
4
|
+
import android.net.Uri
|
|
5
|
+
import com.facebook.react.bridge.Promise
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
7
|
+
import com.facebook.react.common.assets.ReactFontManager
|
|
8
|
+
import java.io.File
|
|
9
|
+
import java.io.InputStream
|
|
10
|
+
import java.net.URL
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Registers a dynamically-linked (OTA) font at runtime so the NanoIconView can
|
|
14
|
+
* resolve it by family name via ReactFontManager — the same registry RN fonts use.
|
|
15
|
+
*
|
|
16
|
+
* Reads the bytes at `uri` (file:// / http(s):// / content:// / plain path),
|
|
17
|
+
* writes them to a cache file, builds a Typeface, and registers it under `family`.
|
|
18
|
+
* Caching/versioning of remote fonts is intentionally out of scope.
|
|
19
|
+
*/
|
|
20
|
+
class NanoIconsFontLoaderModule(reactContext: ReactApplicationContext) :
|
|
21
|
+
NativeNanoIconsFontLoaderSpec(reactContext) {
|
|
22
|
+
|
|
23
|
+
override fun getName(): String = NAME
|
|
24
|
+
|
|
25
|
+
override fun registerFont(family: String, uri: String, promise: Promise) {
|
|
26
|
+
try {
|
|
27
|
+
val cacheFile = File.createTempFile("nanoicon_", ".ttf", reactApplicationContext.cacheDir)
|
|
28
|
+
openStream(uri).use { input ->
|
|
29
|
+
cacheFile.outputStream().use { output -> input.copyTo(output) }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
val typeface = Typeface.createFromFile(cacheFile)
|
|
33
|
+
ReactFontManager.getInstance().setTypeface(family, Typeface.NORMAL, typeface)
|
|
34
|
+
promise.resolve(true)
|
|
35
|
+
} catch (e: Exception) {
|
|
36
|
+
promise.reject(
|
|
37
|
+
"E_NANOICONS_FONT_REGISTER",
|
|
38
|
+
"Failed to register font \"$family\" from $uri: ${e.message}",
|
|
39
|
+
e
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private fun openStream(uri: String): InputStream =
|
|
45
|
+
when {
|
|
46
|
+
uri.startsWith("content://") ->
|
|
47
|
+
reactApplicationContext.contentResolver.openInputStream(Uri.parse(uri))
|
|
48
|
+
?: throw IllegalStateException("Cannot open content uri: $uri")
|
|
49
|
+
uri.startsWith("file://") ||
|
|
50
|
+
uri.startsWith("http://") ||
|
|
51
|
+
uri.startsWith("https://") -> URL(uri).openStream()
|
|
52
|
+
else -> File(uri).inputStream()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
companion object {
|
|
56
|
+
const val NAME = "NanoIconsFontLoader"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -3,6 +3,7 @@ package com.nanoicons
|
|
|
3
3
|
import com.facebook.react.BaseReactPackage
|
|
4
4
|
import com.facebook.react.bridge.NativeModule
|
|
5
5
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfo
|
|
6
7
|
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
7
8
|
import com.facebook.react.uimanager.ViewManager
|
|
8
9
|
|
|
@@ -15,8 +16,24 @@ class NanoIconsPackage : BaseReactPackage() {
|
|
|
15
16
|
override fun getModule(
|
|
16
17
|
name: String,
|
|
17
18
|
reactContext: ReactApplicationContext
|
|
18
|
-
): NativeModule? =
|
|
19
|
+
): NativeModule? =
|
|
20
|
+
when (name) {
|
|
21
|
+
NanoIconsFontLoaderModule.NAME -> NanoIconsFontLoaderModule(reactContext)
|
|
22
|
+
else -> null
|
|
23
|
+
}
|
|
19
24
|
|
|
20
25
|
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider =
|
|
21
|
-
ReactModuleInfoProvider {
|
|
26
|
+
ReactModuleInfoProvider {
|
|
27
|
+
mapOf(
|
|
28
|
+
NanoIconsFontLoaderModule.NAME to
|
|
29
|
+
ReactModuleInfo(
|
|
30
|
+
NanoIconsFontLoaderModule.NAME,
|
|
31
|
+
NanoIconsFontLoaderModule.NAME,
|
|
32
|
+
false, // canOverrideExistingModule
|
|
33
|
+
false, // needsEagerInit
|
|
34
|
+
false, // isCxxModule
|
|
35
|
+
true // isTurboModule
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
}
|
|
22
39
|
}
|
package/ios/NanoIconView.h
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
#import <React/RCTViewComponentView.h>
|
|
2
|
+
#import <Foundation/Foundation.h>
|
|
2
3
|
|
|
3
4
|
@interface NanoIconView : RCTViewComponentView
|
|
4
5
|
@end
|
|
6
|
+
|
|
7
|
+
// Clears cached CTFontRef entries for `family` so the next render fetches a
|
|
8
|
+
// fresh reference after an OTA font re-registration within the same process.
|
|
9
|
+
void NanoIconInvalidateFontCache(NSString *family);
|
package/ios/NanoIconView.mm
CHANGED
|
@@ -24,22 +24,38 @@ using namespace facebook::react;
|
|
|
24
24
|
}
|
|
25
25
|
@end
|
|
26
26
|
|
|
27
|
-
//
|
|
28
|
-
//
|
|
27
|
+
// CTFontRef cache keyed by "family:size" — avoids CTFontCreateWithName per render.
|
|
28
|
+
// File-scope so NanoIconInvalidateFontCache can clear it on OTA font swap.
|
|
29
|
+
static NSMutableDictionary *sNanoIconFontCache;
|
|
30
|
+
static dispatch_once_t sNanoIconFontCacheOnce;
|
|
31
|
+
|
|
29
32
|
static CTFontRef NanoIconGetCachedFont(NSString *family, CGFloat size) {
|
|
30
|
-
|
|
31
|
-
static dispatch_once_t onceToken;
|
|
32
|
-
dispatch_once(&onceToken, ^{ cache = [NSMutableDictionary new]; });
|
|
33
|
+
dispatch_once(&sNanoIconFontCacheOnce, ^{ sNanoIconFontCache = [NSMutableDictionary new]; });
|
|
33
34
|
|
|
34
35
|
NSString *key = [NSString stringWithFormat:@"%@:%.1f", family, size];
|
|
35
|
-
id existing =
|
|
36
|
-
if (existing)
|
|
36
|
+
id existing = sNanoIconFontCache[key];
|
|
37
|
+
if (existing) {
|
|
38
|
+
return (__bridge CTFontRef)existing;
|
|
39
|
+
}
|
|
37
40
|
|
|
38
41
|
CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)family, size, NULL);
|
|
39
|
-
if (font)
|
|
42
|
+
if (font) sNanoIconFontCache[key] = (__bridge id)font;
|
|
40
43
|
return font;
|
|
41
44
|
}
|
|
42
45
|
|
|
46
|
+
// Clears cached entries for `family` after OTA font re-registration.
|
|
47
|
+
void NanoIconInvalidateFontCache(NSString *family) {
|
|
48
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
49
|
+
if (!sNanoIconFontCache) return;
|
|
50
|
+
NSString *prefix = [family stringByAppendingString:@":"];
|
|
51
|
+
NSArray<NSString *> *keys = [sNanoIconFontCache.allKeys
|
|
52
|
+
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *k, id _) {
|
|
53
|
+
return [k hasPrefix:prefix];
|
|
54
|
+
}]];
|
|
55
|
+
[sNanoIconFontCache removeObjectsForKeys:keys];
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
43
59
|
@implementation NanoIconView {
|
|
44
60
|
CTFontRef _font; // borrowed from static cache — do NOT CFRelease
|
|
45
61
|
NSString *_fontFamily;
|
|
@@ -175,11 +191,17 @@ static CTFontRef NanoIconGetCachedFont(NSString *family, CGFloat size) {
|
|
|
175
191
|
_drawingLayer = layer;
|
|
176
192
|
}
|
|
177
193
|
|
|
178
|
-
//
|
|
194
|
+
// Reset inline state when the view moves to a new parent.
|
|
179
195
|
- (void)didMoveToSuperview {
|
|
180
196
|
[super didMoveToSuperview];
|
|
181
197
|
_inlineDetected = NO;
|
|
198
|
+
_isInlineInText = NO;
|
|
199
|
+
_paragraphView = nil;
|
|
182
200
|
_baselineOffsetValid = NO;
|
|
201
|
+
if (_drawingLayer) {
|
|
202
|
+
[_drawingLayer removeFromSuperlayer];
|
|
203
|
+
_drawingLayer = nil;
|
|
204
|
+
}
|
|
183
205
|
}
|
|
184
206
|
|
|
185
207
|
// Invalidate cached offset when size changes (text relayout).
|
|
@@ -339,6 +361,10 @@ static CTFontRef NanoIconGetCachedFont(NSString *family, CGFloat size) {
|
|
|
339
361
|
|
|
340
362
|
// Map Unicode codepoints to font glyph IDs, handling surrogate pairs for codepoints > 0xFFFF.
|
|
341
363
|
if (fontChanged || oldViewProps.codepoints != newViewProps.codepoints) {
|
|
364
|
+
// Re-fetch on codepoint change — cache may have been cleared by OTA font swap.
|
|
365
|
+
if (!fontChanged && _fontFamily) {
|
|
366
|
+
_font = NanoIconGetCachedFont(_fontFamily, _fontSize);
|
|
367
|
+
}
|
|
342
368
|
const auto &codepoints = newViewProps.codepoints;
|
|
343
369
|
_glyphs.resize(codepoints.size());
|
|
344
370
|
for (size_t i = 0; i < codepoints.size(); i++) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
2
|
+
#import <RNNanoIconsSpec/RNNanoIconsSpec.h>
|
|
3
|
+
|
|
4
|
+
@interface NanoIconsFontLoader : NSObject <NativeNanoIconsFontLoaderSpec>
|
|
5
|
+
@end
|
|
6
|
+
#else
|
|
7
|
+
#import <React/RCTBridgeModule.h>
|
|
8
|
+
|
|
9
|
+
@interface NanoIconsFontLoader : NSObject <RCTBridgeModule>
|
|
10
|
+
@end
|
|
11
|
+
#endif
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#import "NanoIconsFontLoader.h"
|
|
2
|
+
#import "NanoIconView.h"
|
|
3
|
+
#import <CoreText/CoreText.h>
|
|
4
|
+
|
|
5
|
+
@implementation NanoIconsFontLoader
|
|
6
|
+
|
|
7
|
+
RCT_EXPORT_MODULE()
|
|
8
|
+
|
|
9
|
+
// `family` must match the TTF's PostScript/full name — CTFontManager ignores it otherwise.
|
|
10
|
+
RCT_EXPORT_METHOD(registerFont:(NSString *)family
|
|
11
|
+
uri:(NSString *)uri
|
|
12
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
13
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
14
|
+
{
|
|
15
|
+
NSURL *url = [uri hasPrefix:@"/"] ? [NSURL fileURLWithPath:uri]
|
|
16
|
+
: [NSURL URLWithString:uri];
|
|
17
|
+
if (!url) {
|
|
18
|
+
reject(@"E_NANOICONS_FONT_REGISTER",
|
|
19
|
+
[NSString stringWithFormat:@"Invalid font uri: %@", uri], nil);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
NSData *data = [NSData dataWithContentsOfURL:url];
|
|
24
|
+
if (!data) {
|
|
25
|
+
reject(@"E_NANOICONS_FONT_REGISTER",
|
|
26
|
+
[NSString stringWithFormat:@"Could not read font at %@", uri], nil);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
|
|
31
|
+
CGFontRef cgFont = CGFontCreateWithDataProvider(provider);
|
|
32
|
+
CGDataProviderRelease(provider);
|
|
33
|
+
if (!cgFont) {
|
|
34
|
+
reject(@"E_NANOICONS_FONT_REGISTER", @"Invalid font data", nil);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
NSString *postScriptName = (__bridge_transfer NSString *)CGFontCopyPostScriptName(cgFont);
|
|
39
|
+
NSString *fullName = (__bridge_transfer NSString *)CGFontCopyFullName(cgFont);
|
|
40
|
+
|
|
41
|
+
BOOL nameMatches =
|
|
42
|
+
[postScriptName isEqualToString:family] || [fullName isEqualToString:family];
|
|
43
|
+
if (!nameMatches) {
|
|
44
|
+
CGFontRelease(cgFont);
|
|
45
|
+
reject(
|
|
46
|
+
@"E_NANOICONS_FONT_REGISTER",
|
|
47
|
+
[NSString stringWithFormat:
|
|
48
|
+
@"Font name \"%@\" does not match family \"%@\". "
|
|
49
|
+
@"On iOS the TTF PostScript/full name must equal glyphMap.m.f.",
|
|
50
|
+
postScriptName ?: fullName, family],
|
|
51
|
+
nil);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
CFErrorRef error = NULL;
|
|
56
|
+
bool ok = CTFontManagerRegisterGraphicsFont(cgFont, &error);
|
|
57
|
+
// defer cgFont release — needed for re-registration if name conflict.
|
|
58
|
+
|
|
59
|
+
if (!ok && error) {
|
|
60
|
+
NSError *err = (__bridge_transfer NSError *)error;
|
|
61
|
+
|
|
62
|
+
if (err.code == kCTFontManagerErrorAlreadyRegistered ||
|
|
63
|
+
err.code == kCTFontManagerErrorDuplicatedName) {
|
|
64
|
+
// OTA reload: process still holds the old font. Swap it out.
|
|
65
|
+
CTFontRef existingCT = CTFontCreateWithName((__bridge CFStringRef)postScriptName, 10.0, NULL);
|
|
66
|
+
if (existingCT) {
|
|
67
|
+
CGFontRef existingCG = CTFontCopyGraphicsFont(existingCT, NULL);
|
|
68
|
+
CFRelease(existingCT);
|
|
69
|
+
if (existingCG) {
|
|
70
|
+
CFErrorRef unregErr = NULL;
|
|
71
|
+
CTFontManagerUnregisterGraphicsFont(existingCG, &unregErr);
|
|
72
|
+
CFRelease(existingCG);
|
|
73
|
+
if (unregErr) CFRelease(unregErr);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
CFErrorRef regErr = NULL;
|
|
78
|
+
bool reok = CTFontManagerRegisterGraphicsFont(cgFont, ®Err);
|
|
79
|
+
CGFontRelease(cgFont);
|
|
80
|
+
|
|
81
|
+
if (!reok && regErr) {
|
|
82
|
+
NSError *reErr = (__bridge_transfer NSError *)regErr;
|
|
83
|
+
reject(@"E_NANOICONS_FONT_REGISTER", reErr.localizedDescription, reErr);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (regErr) CFRelease(regErr);
|
|
87
|
+
|
|
88
|
+
NanoIconInvalidateFontCache(postScriptName);
|
|
89
|
+
resolve(@(YES));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
CGFontRelease(cgFont);
|
|
94
|
+
reject(@"E_NANOICONS_FONT_REGISTER", err.localizedDescription, err);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
CGFontRelease(cgFont);
|
|
99
|
+
resolve(@(YES));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
103
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
104
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
105
|
+
{
|
|
106
|
+
return std::make_shared<facebook::react::NativeNanoIconsFontLoaderSpecJSI>(params);
|
|
107
|
+
}
|
|
108
|
+
#endif
|
|
109
|
+
|
|
110
|
+
@end
|
|
@@ -12,11 +12,14 @@ export type IconSetConfig = {
|
|
|
12
12
|
safeZone?: number;
|
|
13
13
|
/** First Unicode codepoint for glyphs (default 0xe900). Hex string or number. */
|
|
14
14
|
startUnicode?: number | string;
|
|
15
|
+
/** Linking type for the font (default 'static'). Static bundles the TTF, dynamic delivers it via OTA*/
|
|
16
|
+
linking?: 'static' | 'dynamic';
|
|
15
17
|
};
|
|
16
18
|
export type BuiltFont = {
|
|
17
19
|
fontFamily: string;
|
|
18
20
|
ttfPath: string;
|
|
19
21
|
glyphmapPath: string;
|
|
22
|
+
linking: 'static' | 'dynamic';
|
|
20
23
|
};
|
|
21
24
|
/**
|
|
22
25
|
* Build TTF + glyphmap for all icon sets using a single Pyodide/PathKit instance.
|
|
@@ -11,7 +11,7 @@ const fingerPrint_js_1 = require("../src/utils/fingerPrint.js");
|
|
|
11
11
|
const DEFAULT_SAFE_ZONE = 1020;
|
|
12
12
|
const DEFAULT_UPM = 1024;
|
|
13
13
|
const DEFAULT_START_UNICODE = 0xe900;
|
|
14
|
-
function shouldSkipGeneration(inputHash, outputDir, fontFamily, logger) {
|
|
14
|
+
function shouldSkipGeneration(inputHash, outputDir, fontFamily, linking, logger) {
|
|
15
15
|
const ttfPath = path_1.default.join(outputDir, `${fontFamily}.ttf`);
|
|
16
16
|
const glyphmapPath = path_1.default.join(outputDir, `${fontFamily}.glyphmap.json`);
|
|
17
17
|
if (!fs_1.default.existsSync(outputDir) ||
|
|
@@ -21,7 +21,8 @@ function shouldSkipGeneration(inputHash, outputDir, fontFamily, logger) {
|
|
|
21
21
|
}
|
|
22
22
|
const glyphmap = JSON.parse(fs_1.default.readFileSync(glyphmapPath, 'utf8'));
|
|
23
23
|
const storedHash = glyphmap?.m?.h;
|
|
24
|
-
|
|
24
|
+
const storedLinking = glyphmap?.m?.l === 'd' ? 'dynamic' : 'static';
|
|
25
|
+
if (storedHash && storedHash === inputHash && storedLinking === linking) {
|
|
25
26
|
logger?.info(`${fontFamily}: SVG fingerprint unchanged, skipping build.`);
|
|
26
27
|
return true;
|
|
27
28
|
}
|
|
@@ -40,6 +41,7 @@ async function buildAllFonts(iconSets, projectRoot, options) {
|
|
|
40
41
|
const set = iconSets[i];
|
|
41
42
|
const inputDir = path_1.default.resolve(projectRoot, set.inputDir);
|
|
42
43
|
const fontFamily = set.fontFamily ?? path_1.default.basename(inputDir);
|
|
44
|
+
const linking = set.linking ?? 'static';
|
|
43
45
|
if (!fs_1.default.existsSync(inputDir)) {
|
|
44
46
|
throw new Error(`[react-native-nano-icons] Input directory does not exist: ${inputDir} (from "${set.inputDir}")`);
|
|
45
47
|
}
|
|
@@ -49,8 +51,8 @@ async function buildAllFonts(iconSets, projectRoot, options) {
|
|
|
49
51
|
const ttfPath = path_1.default.join(outputDir, `${fontFamily}.ttf`);
|
|
50
52
|
const glyphmapPath = path_1.default.join(outputDir, `${fontFamily}.glyphmap.json`);
|
|
51
53
|
const inputHash = (0, fingerPrint_js_1.getFingerprintSync)(inputDir);
|
|
52
|
-
if (shouldSkipGeneration(inputHash, outputDir, fontFamily, logger)) {
|
|
53
|
-
results.push({ fontFamily, ttfPath, glyphmapPath });
|
|
54
|
+
if (shouldSkipGeneration(inputHash, outputDir, fontFamily, linking, logger)) {
|
|
55
|
+
results.push({ fontFamily, ttfPath, glyphmapPath, linking });
|
|
54
56
|
continue;
|
|
55
57
|
}
|
|
56
58
|
if (fs_1.default.existsSync(ttfPath))
|
|
@@ -68,6 +70,7 @@ async function buildAllFonts(iconSets, projectRoot, options) {
|
|
|
68
70
|
? parseInt(set.startUnicode, 16)
|
|
69
71
|
: set.startUnicode
|
|
70
72
|
: DEFAULT_START_UNICODE,
|
|
73
|
+
linking,
|
|
71
74
|
};
|
|
72
75
|
logger?.start(`Building ${fontFamily} (${i + 1}/${iconSets.length})…`);
|
|
73
76
|
const out = await (0, index_js_1.runPipeline)(config, { inputDir, outputDir, tempDir }, { logger, inputHash });
|
|
@@ -75,6 +78,7 @@ async function buildAllFonts(iconSets, projectRoot, options) {
|
|
|
75
78
|
fontFamily,
|
|
76
79
|
ttfPath: out.ttfPath,
|
|
77
80
|
glyphmapPath: out.glyphmapPath,
|
|
81
|
+
linking,
|
|
78
82
|
});
|
|
79
83
|
}
|
|
80
84
|
if (allSkipped && results.length > 0) {
|
|
@@ -7,3 +7,4 @@ export type NanoIconsConfig = {
|
|
|
7
7
|
* Throws with a helpful message if the file is missing or malformed.
|
|
8
8
|
*/
|
|
9
9
|
export declare function loadNanoIconsConfig(configRoot: string): NanoIconsConfig;
|
|
10
|
+
export declare function loadDynamicIconSets(configRoot: string): IconSetConfig[];
|
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.loadNanoIconsConfig = loadNanoIconsConfig;
|
|
7
|
+
exports.loadDynamicIconSets = loadDynamicIconSets;
|
|
7
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
10
|
/**
|
|
@@ -24,3 +25,12 @@ function loadNanoIconsConfig(configRoot) {
|
|
|
24
25
|
}
|
|
25
26
|
return config;
|
|
26
27
|
}
|
|
28
|
+
function loadDynamicIconSets(configRoot) {
|
|
29
|
+
const config = loadNanoIconsConfig(configRoot);
|
|
30
|
+
const dynamicSets = config.iconSets.filter((s) => s.linking === 'dynamic');
|
|
31
|
+
if (dynamicSets.length === 0) {
|
|
32
|
+
throw new Error(`[react-native-nano-icons] No icon sets with linking: "dynamic" found in .nanoicons.json.\n` +
|
|
33
|
+
`--dynamic only processes icon sets where linking is set to "dynamic".`);
|
|
34
|
+
}
|
|
35
|
+
return dynamicSets;
|
|
36
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IconSetConfig } from './build.js';
|
|
2
|
+
/**
|
|
3
|
+
* Load icon sets configured with linking: 'dynamic' from the Expo app config
|
|
4
|
+
* (app.json, app.config.js, or app.config.ts).
|
|
5
|
+
*
|
|
6
|
+
* Requires @expo/config to be installed (present in all Expo projects).
|
|
7
|
+
*/
|
|
8
|
+
export declare function loadDynamicSetsFromAppConfig(projectRoot: string): IconSetConfig[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadDynamicSetsFromAppConfig = loadDynamicSetsFromAppConfig;
|
|
4
|
+
/**
|
|
5
|
+
* Load icon sets configured with linking: 'dynamic' from the Expo app config
|
|
6
|
+
* (app.json, app.config.js, or app.config.ts).
|
|
7
|
+
*
|
|
8
|
+
* Requires @expo/config to be installed (present in all Expo projects).
|
|
9
|
+
*/
|
|
10
|
+
function loadDynamicSetsFromAppConfig(projectRoot) {
|
|
11
|
+
let getConfig;
|
|
12
|
+
try {
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
14
|
+
({ getConfig } = require('@expo/config'));
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new Error(`[react-native-nano-icons] @expo/config not found — required for --dynamic --app-config.\n` +
|
|
18
|
+
`It should already be present in Expo projects. If missing: yarn add @expo/config`);
|
|
19
|
+
}
|
|
20
|
+
const { exp } = getConfig(projectRoot, { skipSDKVersionRequirement: true });
|
|
21
|
+
const plugins = Array.isArray(exp.plugins) ? exp.plugins : [];
|
|
22
|
+
const entry = plugins.find((p) => Array.isArray(p) && p[0] === 'react-native-nano-icons');
|
|
23
|
+
if (!entry) {
|
|
24
|
+
throw new Error(`[react-native-nano-icons] Plugin "react-native-nano-icons" not found in app config.\n` +
|
|
25
|
+
`Add it to your app.json or app.config.js/ts under the "plugins" key.`);
|
|
26
|
+
}
|
|
27
|
+
const [, options] = entry;
|
|
28
|
+
const dynamicSets = (options.iconSets ?? []).filter((s) => s.linking === 'dynamic');
|
|
29
|
+
if (dynamicSets.length === 0) {
|
|
30
|
+
throw new Error(`[react-native-nano-icons] No icon sets with linking: "dynamic" found.\n` +
|
|
31
|
+
`--dynamic --app-config only processes icon sets where linking is set to "dynamic".`);
|
|
32
|
+
}
|
|
33
|
+
return dynamicSets;
|
|
34
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { buildAllFonts, type IconSetConfig, type BuiltFont } from './build.js';
|
|
2
2
|
export { createOraLogger, createQuietLogger, detectExpoLogLevel, type NanoLogger, type LogLevel, } from './logger.js';
|
|
3
|
-
export { loadNanoIconsConfig, type NanoIconsConfig } from './config.js';
|
|
3
|
+
export { loadNanoIconsConfig, loadDynamicIconSets, type NanoIconsConfig, } from './config.js';
|
|
4
|
+
export { loadDynamicSetsFromAppConfig } from './expoConfig.js';
|
|
4
5
|
export { linkBare } from './link.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.linkBare = exports.loadNanoIconsConfig = exports.detectExpoLogLevel = exports.createQuietLogger = exports.createOraLogger = exports.buildAllFonts = void 0;
|
|
3
|
+
exports.linkBare = exports.loadDynamicSetsFromAppConfig = exports.loadDynamicIconSets = exports.loadNanoIconsConfig = exports.detectExpoLogLevel = exports.createQuietLogger = exports.createOraLogger = exports.buildAllFonts = void 0;
|
|
4
4
|
var build_js_1 = require("./build.js");
|
|
5
5
|
Object.defineProperty(exports, "buildAllFonts", { enumerable: true, get: function () { return build_js_1.buildAllFonts; } });
|
|
6
6
|
var logger_js_1 = require("./logger.js");
|
|
@@ -9,5 +9,8 @@ Object.defineProperty(exports, "createQuietLogger", { enumerable: true, get: fun
|
|
|
9
9
|
Object.defineProperty(exports, "detectExpoLogLevel", { enumerable: true, get: function () { return logger_js_1.detectExpoLogLevel; } });
|
|
10
10
|
var config_js_1 = require("./config.js");
|
|
11
11
|
Object.defineProperty(exports, "loadNanoIconsConfig", { enumerable: true, get: function () { return config_js_1.loadNanoIconsConfig; } });
|
|
12
|
+
Object.defineProperty(exports, "loadDynamicIconSets", { enumerable: true, get: function () { return config_js_1.loadDynamicIconSets; } });
|
|
13
|
+
var expoConfig_js_1 = require("./expoConfig.js");
|
|
14
|
+
Object.defineProperty(exports, "loadDynamicSetsFromAppConfig", { enumerable: true, get: function () { return expoConfig_js_1.loadDynamicSetsFromAppConfig; } });
|
|
12
15
|
var link_js_1 = require("./link.js");
|
|
13
16
|
Object.defineProperty(exports, "linkBare", { enumerable: true, get: function () { return link_js_1.linkBare; } });
|