react-native-nano-icons 0.0.0 → 0.1.1
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/LICENSE +21 -0
- package/README.md +174 -0
- package/app.plugin.js +1 -0
- package/lib/commonjs/cli/build.d.ts +28 -0
- package/lib/commonjs/cli/build.js +84 -0
- package/lib/commonjs/cli/config.d.ts +9 -0
- package/lib/commonjs/cli/config.js +25 -0
- package/lib/commonjs/cli/index.d.ts +4 -0
- package/lib/commonjs/cli/index.js +13 -0
- package/lib/commonjs/cli/link.d.ts +11 -0
- package/lib/commonjs/cli/link.js +135 -0
- package/lib/commonjs/cli/logger.d.ts +27 -0
- package/lib/commonjs/cli/logger.js +83 -0
- package/lib/commonjs/index.node.js +8 -0
- package/lib/commonjs/plugin/src/buildFonts.d.ts +7 -0
- package/lib/commonjs/plugin/src/buildFonts.js +23 -0
- package/lib/commonjs/plugin/src/index.d.ts +5 -0
- package/lib/commonjs/plugin/src/index.js +43 -0
- package/lib/commonjs/plugin/src/types.d.ts +31 -0
- package/lib/commonjs/plugin/src/types.js +2 -0
- package/lib/commonjs/plugin/src/withNanoIconsFontLinking.d.ts +14 -0
- package/lib/commonjs/plugin/src/withNanoIconsFontLinking.js +92 -0
- package/lib/commonjs/scripts/cli.js +28 -0
- package/lib/commonjs/src/const/colors.d.ts +1 -0
- package/lib/commonjs/src/const/colors.js +153 -0
- package/lib/commonjs/src/core/font/compile.d.ts +9 -0
- package/lib/commonjs/src/core/font/compile.js +68 -0
- package/lib/commonjs/src/core/font/metrics.d.ts +1 -0
- package/lib/commonjs/src/core/font/metrics.js +33 -0
- package/lib/commonjs/src/core/pipeline/config.d.ts +14 -0
- package/lib/commonjs/src/core/pipeline/config.js +17 -0
- package/lib/commonjs/src/core/pipeline/index.d.ts +3 -0
- package/lib/commonjs/src/core/pipeline/index.js +7 -0
- package/lib/commonjs/src/core/pipeline/managers.d.ts +11 -0
- package/lib/commonjs/src/core/pipeline/managers.js +89 -0
- package/lib/commonjs/src/core/pipeline/run.d.ts +14 -0
- package/lib/commonjs/src/core/pipeline/run.js +99 -0
- package/lib/commonjs/src/core/svg/layers.d.ts +24 -0
- package/lib/commonjs/src/core/svg/layers.js +42 -0
- package/lib/commonjs/src/core/svg/svg_dom.d.ts +22 -0
- package/lib/commonjs/src/core/svg/svg_dom.js +84 -0
- package/lib/commonjs/src/core/svg/svg_pathops.d.ts +21 -0
- package/lib/commonjs/src/core/svg/svg_pathops.js +611 -0
- package/lib/commonjs/src/core/types.d.ts +120 -0
- package/lib/commonjs/src/core/types.js +2 -0
- package/lib/commonjs/src/utils/fingerPrint.d.ts +1 -0
- package/lib/commonjs/src/utils/fingerPrint.js +20 -0
- package/lib/commonjs/src/utils/parse.d.ts +2 -0
- package/lib/commonjs/src/utils/parse.js +64 -0
- package/lib/module/const/colors.js +153 -0
- package/lib/module/const/colors.js.map +1 -0
- package/lib/module/core/font/compile.js +70 -0
- package/lib/module/core/font/compile.js.map +1 -0
- package/lib/module/core/font/metrics.js +37 -0
- package/lib/module/core/font/metrics.js.map +1 -0
- package/lib/module/core/pipeline/config.js +20 -0
- package/lib/module/core/pipeline/config.js.map +1 -0
- package/lib/module/core/pipeline/index.js +5 -0
- package/lib/module/core/pipeline/index.js.map +1 -0
- package/lib/module/core/pipeline/managers.js +80 -0
- package/lib/module/core/pipeline/managers.js.map +1 -0
- package/lib/module/core/pipeline/run.js +114 -0
- package/lib/module/core/pipeline/run.js.map +1 -0
- package/lib/module/core/shims/pathops.py +181 -0
- package/lib/module/core/svg/layers.js +51 -0
- package/lib/module/core/svg/layers.js.map +1 -0
- package/lib/module/core/svg/svg_dom.js +83 -0
- package/lib/module/core/svg/svg_dom.js.map +1 -0
- package/lib/module/core/svg/svg_pathops.js +566 -0
- package/lib/module/core/svg/svg_pathops.js.map +1 -0
- package/lib/module/core/tsconfig.json +32 -0
- package/lib/module/core/types.js +2 -0
- package/lib/module/core/types.js.map +1 -0
- package/lib/module/createNanoIconsSet.js +84 -0
- package/lib/module/createNanoIconsSet.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/index.node.js +13 -0
- package/lib/module/index.node.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/utils/fingerPrint.js +17 -0
- package/lib/module/utils/fingerPrint.js.map +1 -0
- package/lib/module/utils/parse.js +53 -0
- package/lib/module/utils/parse.js.map +1 -0
- package/lib/typescript/__tests__/build.unit.test.d.ts +3 -0
- package/lib/typescript/__tests__/build.unit.test.d.ts.map +1 -0
- package/lib/typescript/__tests__/clippath.e2e.test.d.ts +3 -0
- package/lib/typescript/__tests__/clippath.e2e.test.d.ts.map +1 -0
- package/lib/typescript/__tests__/fingerprint.unit.test.d.ts +3 -0
- package/lib/typescript/__tests__/fingerprint.unit.test.d.ts.map +1 -0
- package/lib/typescript/__tests__/pipeline.e2e.test.d.ts +3 -0
- package/lib/typescript/__tests__/pipeline.e2e.test.d.ts.map +1 -0
- package/lib/typescript/__tests__/placement.unit.test.d.ts +3 -0
- package/lib/typescript/__tests__/placement.unit.test.d.ts.map +1 -0
- package/lib/typescript/__tests__/svg_dom.unit.test.d.ts +3 -0
- package/lib/typescript/__tests__/svg_dom.unit.test.d.ts.map +1 -0
- package/lib/typescript/cli/build.d.ts +29 -0
- package/lib/typescript/cli/build.d.ts.map +1 -0
- package/lib/typescript/cli/logger.d.ts +28 -0
- package/lib/typescript/cli/logger.d.ts.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/const/colors.d.ts +2 -0
- package/lib/typescript/src/const/colors.d.ts.map +1 -0
- package/lib/typescript/src/core/font/compile.d.ts +10 -0
- package/lib/typescript/src/core/font/compile.d.ts.map +1 -0
- package/lib/typescript/src/core/font/metrics.d.ts +2 -0
- package/lib/typescript/src/core/font/metrics.d.ts.map +1 -0
- package/lib/typescript/src/core/pipeline/config.d.ts +15 -0
- package/lib/typescript/src/core/pipeline/config.d.ts.map +1 -0
- package/lib/typescript/src/core/pipeline/index.d.ts +4 -0
- package/lib/typescript/src/core/pipeline/index.d.ts.map +1 -0
- package/lib/typescript/src/core/pipeline/managers.d.ts +12 -0
- package/lib/typescript/src/core/pipeline/managers.d.ts.map +1 -0
- package/lib/typescript/src/core/pipeline/run.d.ts +15 -0
- package/lib/typescript/src/core/pipeline/run.d.ts.map +1 -0
- package/lib/typescript/src/core/svg/layers.d.ts +25 -0
- package/lib/typescript/src/core/svg/layers.d.ts.map +1 -0
- package/lib/typescript/src/core/svg/svg_dom.d.ts +23 -0
- package/lib/typescript/src/core/svg/svg_dom.d.ts.map +1 -0
- package/lib/typescript/src/core/svg/svg_pathops.d.ts +22 -0
- package/lib/typescript/src/core/svg/svg_pathops.d.ts.map +1 -0
- package/lib/typescript/src/core/types.d.ts +121 -0
- package/lib/typescript/src/core/types.d.ts.map +1 -0
- package/lib/typescript/src/createNanoIconsSet.d.ts +19 -0
- package/lib/typescript/src/createNanoIconsSet.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/index.node.d.ts +2 -0
- package/lib/typescript/src/index.node.d.ts.map +1 -0
- package/lib/typescript/src/utils/fingerPrint.d.ts +2 -0
- package/lib/typescript/src/utils/fingerPrint.d.ts.map +1 -0
- package/lib/typescript/src/utils/parse.d.ts +3 -0
- package/lib/typescript/src/utils/parse.d.ts.map +1 -0
- package/package.json +160 -1
- package/plugin/src/buildFonts.ts +29 -0
- package/plugin/src/index.ts +68 -0
- package/plugin/src/types.ts +33 -0
- package/plugin/src/withNanoIconsFontLinking.ts +119 -0
- package/plugin/tsconfig.json +9 -0
- package/scripts/cli.ts +34 -0
- package/scripts/tsconfig.json +25 -0
- package/src/const/colors.ts +150 -0
- package/src/core/font/compile.ts +96 -0
- package/src/core/font/metrics.ts +44 -0
- package/src/core/pipeline/config.ts +24 -0
- package/src/core/pipeline/index.ts +3 -0
- package/src/core/pipeline/managers.ts +114 -0
- package/src/core/pipeline/run.ts +151 -0
- package/src/core/shims/pathops.py +181 -0
- package/src/core/svg/layers.ts +66 -0
- package/src/core/svg/svg_dom.ts +99 -0
- package/src/core/svg/svg_pathops.ts +796 -0
- package/src/core/tsconfig.json +32 -0
- package/src/core/types.ts +138 -0
- package/src/createNanoIconsSet.tsx +131 -0
- package/src/index.node.ts +14 -0
- package/src/index.ts +7 -0
- package/src/utils/fingerPrint.ts +20 -0
- package/src/utils/parse.ts +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Software Mansion <swmansion.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# react-native-nano-icons
|
|
2
|
+
|
|
3
|
+
**High-performance, build-time icon font generation for React Native & Expo.**
|
|
4
|
+
|
|
5
|
+
`react-native-nano-icons` automates the conversion of SVG directories into optimized, **multi-color-aware** native fonts and strictly typed TypeScript component factories. It leverages a WebAssembly-powered skia pathops binary build pipeline to ensure pixel-perfect geometry and zero runtime overhead.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🧩 Platforms Supported
|
|
10
|
+
|
|
11
|
+
- [x] iOS
|
|
12
|
+
- [x] Android
|
|
13
|
+
- [x] Web
|
|
14
|
+
|
|
15
|
+
> [!NOTE]
|
|
16
|
+
> `<filter>` and `<mask>` are not yet supported, due to native fonts' glyph limitations.
|
|
17
|
+
> In order to leverage those features, use [`react-native-svg`](https://github.com/software-mansion/react-native-svg) or [`expo-image`](https://docs.expo.dev/versions/latest/sdk/image/)
|
|
18
|
+
|
|
19
|
+
## 🚀 Usage
|
|
20
|
+
|
|
21
|
+
### 1. Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install react-native-nano-icons
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 2. Configuration
|
|
28
|
+
|
|
29
|
+
#### 2.1. Expo
|
|
30
|
+
|
|
31
|
+
The library uses an Expo Config Plugin to hook into the prebuild phase. This automatically generates the `.ttf` and corresponding `glyphmap` files and links them to the native iOS/Android project's assets.
|
|
32
|
+
|
|
33
|
+
`app.json`
|
|
34
|
+
|
|
35
|
+
```JSON
|
|
36
|
+
{
|
|
37
|
+
"expo": {
|
|
38
|
+
"plugins": [
|
|
39
|
+
[
|
|
40
|
+
"react-native-nano-icons",
|
|
41
|
+
{
|
|
42
|
+
"iconSets": [
|
|
43
|
+
{
|
|
44
|
+
"inputDir": "./assets/icons/user"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
<details>
|
|
55
|
+
<summary>All iconSets Entry Plugin Options</summary>
|
|
56
|
+
|
|
57
|
+
The plugin accepts an object with an `iconSets` array, allowing you to generate multiple distinct fonts in a single build.
|
|
58
|
+
|
|
59
|
+
| Property | Type | Required | Default | Description |
|
|
60
|
+
| :------------- | :------- | :------- | :------------- | :------------------------------------------------------------------------------------------------------------------------- |
|
|
61
|
+
| `inputDir` | `string` | **Yes** | — | Path to the directory containing your `.svg` files (e.g., `./assets/icons/ui`). |
|
|
62
|
+
| `fontFamily` | `string` | No | Folder Name | The name of the generated font family and file. If omitted, the name of the `inputDir` folder is used (e.g., `ui`). |
|
|
63
|
+
| `outputDir` | `string` | No | `../nanoicons` | Path where the `.ttf` and `.json` artifacts will be saved. Defaults to a sibling `nanoicons` folder relative to the input. |
|
|
64
|
+
| `upm` | `number` | No | `1024` | Units Per Em. Defines the resolution of the font grid. |
|
|
65
|
+
| `startUnicode` | `string` | No | `0xe900` | The starting Hex Unicode point for the first icon glyph. |
|
|
66
|
+
|
|
67
|
+
<details>
|
|
68
|
+
<summary>Default Dir Path Behavior</summary>
|
|
69
|
+
If you do not specify an `outputDir` or `fontFamily`, the library attempts to keep your project organized by creating a sibling folder.
|
|
70
|
+
|
|
71
|
+
- **Input:** `./assets/icons/user`
|
|
72
|
+
- **Resulting Output:** `./assets/icons/nanoicons/user.ttf` & `user.glyphmap.json`
|
|
73
|
+
</details>
|
|
74
|
+
</details>
|
|
75
|
+
|
|
76
|
+
#### 2.2 Bare React Native/React Native Web (no Expo)
|
|
77
|
+
|
|
78
|
+
Bare apps don’t have a prebuild step, so you run the same pipeline via the CLI and ship the built fonts into the native project yourself:
|
|
79
|
+
|
|
80
|
+
1. **Config** – At the app root, add a `.nanoicons.json` with the same `iconSets` shape as the Expo plugin (see "All iconSets Entry Plugin Options above").
|
|
81
|
+
<details>
|
|
82
|
+
<summary>.nanoicons.json example</summary>
|
|
83
|
+
|
|
84
|
+
```JSON
|
|
85
|
+
{
|
|
86
|
+
"iconSets": [
|
|
87
|
+
{
|
|
88
|
+
"inputDir": "./assets/icons/user"
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
</details>
|
|
95
|
+
|
|
96
|
+
2. **Build and link** – From the app root run:
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
npx react-native-nano-icons
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This works exactly like the config plugin, removing any necessity for manual Xcode/Android Studio steps.
|
|
103
|
+
|
|
104
|
+
### 3. Creating an Icon Set Component
|
|
105
|
+
|
|
106
|
+
Use the factory function to create a fully typed component for your specific icon set. This enables multiple distinct sets (e.g., "Outlined", "Solid", "Brand") within a single app.
|
|
107
|
+
|
|
108
|
+
`src/components/UserIcon.tsx`
|
|
109
|
+
|
|
110
|
+
```TypeScript
|
|
111
|
+
import { createNanoIconSet } from "react-native-nano-icons";
|
|
112
|
+
// auto-generated during build-time in outputDir
|
|
113
|
+
import glyphMap from "../../assets/nanoicons/UserIcons.glyphmap.json";
|
|
114
|
+
|
|
115
|
+
export const UserIcon = createNanoIconSet(glyphMap);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 4. Component Usage
|
|
119
|
+
|
|
120
|
+
The generated component supports standard `Text` props **excluding** `style.color | .fontWeight | .fontFamily`.
|
|
121
|
+
|
|
122
|
+
To manipulate the color(s) of the icon you should provide `colorPalette: ColorValue[]`.
|
|
123
|
+
|
|
124
|
+
The `name` prop corresponds to **the original name of the svg file** for a given icon.
|
|
125
|
+
|
|
126
|
+
```TypeScript
|
|
127
|
+
import { Text } from 'react-native'
|
|
128
|
+
import { UserIcon } from './components/UserIcon';
|
|
129
|
+
|
|
130
|
+
export default function App() {
|
|
131
|
+
return (
|
|
132
|
+
<>
|
|
133
|
+
// Renders the icon with its original multi-color layers from the SVG
|
|
134
|
+
<UserIcon name="avatar-1" size={32} />
|
|
135
|
+
|
|
136
|
+
// Overrides all color layers with the provided colors respectively
|
|
137
|
+
<UserIcon name="avatar-1" size={24} colorPalette={["blue", "#ffffff", "#fc2930"]} />
|
|
138
|
+
|
|
139
|
+
// Renders icon inline within a paragraph
|
|
140
|
+
<Text>
|
|
141
|
+
User <UserIcon name="avatar-1" size={12} /> liked your photo!
|
|
142
|
+
</Text>
|
|
143
|
+
</>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Your color icons can have as many colors as your original svg has, therefore you should experiment to establish which element of the array corresponds to the layer you aim to change the color of.
|
|
149
|
+
If the icon is single-color by design (which results in creating a single glyph during build-time) only the first element is took into consideration, and if the `colorPalette` array is too short - the last color is repeated.
|
|
150
|
+
|
|
151
|
+
### 5. Font Regeneration
|
|
152
|
+
|
|
153
|
+
The script detects changes in path and contents of the SVGs in your input directory based on a fingerprint hash. If anything changed, or the output font files are deleted, a given icon-set is regenerated.
|
|
154
|
+
|
|
155
|
+
> [!IMPORTANT]
|
|
156
|
+
> **You should always verify your icons visually.**
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 💡 Architecture & Pipeline & Examples
|
|
161
|
+
|
|
162
|
+
see [MOTIVATION.md](docs/MOTIVATION.md)
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
`react-native-nano-icons` is released under the **MIT License**. See [LICENSE](LICENSE) for the full text.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
Built by [Software Mansion](https://swmansion.com/).
|
|
173
|
+
|
|
174
|
+
[<img width="128" height="69" alt="Software Mansion Logo" src="https://github.com/user-attachments/assets/f0e18471-a7aa-4e80-86ac-87686a86fe56" />](https://swmansion.com/)
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./lib/commonjs/plugin/src/index').default;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { NanoLogger } from './logger.js';
|
|
2
|
+
export type IconSetConfig = {
|
|
3
|
+
/** Path to folder of SVG files (relative to project root). */
|
|
4
|
+
inputDir: string;
|
|
5
|
+
/** Font family name (used for TTF and glyphmap filenames). */
|
|
6
|
+
fontFamily?: string;
|
|
7
|
+
/** Path where .ttf and .glyphmap.json will be saved. Defaults to a sibling nanoicons folder relative to inputDir. */
|
|
8
|
+
outputDir?: string;
|
|
9
|
+
/** Units per em (default 1024). */
|
|
10
|
+
upm?: number;
|
|
11
|
+
/** Safe zone inside UPM for glyphs (default 1020). */
|
|
12
|
+
safeZone?: number;
|
|
13
|
+
/** First Unicode codepoint for glyphs (default 0xe900). Hex string or number. */
|
|
14
|
+
startUnicode?: number | string;
|
|
15
|
+
};
|
|
16
|
+
export type BuiltFont = {
|
|
17
|
+
fontFamily: string;
|
|
18
|
+
ttfPath: string;
|
|
19
|
+
glyphmapPath: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Build TTF + glyphmap for all icon sets using a single Pyodide/PathKit instance.
|
|
23
|
+
* Output is placed in a "nanoicons" folder next to each input dir (sibling to inputDir).
|
|
24
|
+
* Skips generation for a set if that output folder already contains the expected .ttf and .glyphmap.json.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildAllFonts(iconSets: IconSetConfig[], projectRoot: string, options?: {
|
|
27
|
+
logger?: NanoLogger;
|
|
28
|
+
}): Promise<BuiltFont[]>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildAllFonts = buildAllFonts;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const index_js_1 = require("../src/core/pipeline/index.js");
|
|
10
|
+
const fingerPrint_js_1 = require("../src/utils/fingerPrint.js");
|
|
11
|
+
const DEFAULT_SAFE_ZONE = 1020;
|
|
12
|
+
const DEFAULT_UPM = 1024;
|
|
13
|
+
const DEFAULT_START_UNICODE = 0xe900;
|
|
14
|
+
function shouldSkipGeneration(inputHash, outputDir, fontFamily, logger) {
|
|
15
|
+
const ttfPath = path_1.default.join(outputDir, `${fontFamily}.ttf`);
|
|
16
|
+
const glyphmapPath = path_1.default.join(outputDir, `${fontFamily}.glyphmap.json`);
|
|
17
|
+
if (!fs_1.default.existsSync(outputDir) ||
|
|
18
|
+
!fs_1.default.existsSync(ttfPath) ||
|
|
19
|
+
!fs_1.default.existsSync(glyphmapPath)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const glyphmap = JSON.parse(fs_1.default.readFileSync(glyphmapPath, 'utf8'));
|
|
23
|
+
const storedHash = glyphmap?.meta?.hash;
|
|
24
|
+
if (storedHash && storedHash === inputHash) {
|
|
25
|
+
logger?.info(`${fontFamily}: SVG fingerprint unchanged, skipping build.`);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Build TTF + glyphmap for all icon sets using a single Pyodide/PathKit instance.
|
|
32
|
+
* Output is placed in a "nanoicons" folder next to each input dir (sibling to inputDir).
|
|
33
|
+
* Skips generation for a set if that output folder already contains the expected .ttf and .glyphmap.json.
|
|
34
|
+
*/
|
|
35
|
+
async function buildAllFonts(iconSets, projectRoot, options) {
|
|
36
|
+
const logger = options?.logger;
|
|
37
|
+
const results = [];
|
|
38
|
+
let allSkipped = true;
|
|
39
|
+
for (let i = 0; i < iconSets.length; i++) {
|
|
40
|
+
const set = iconSets[i];
|
|
41
|
+
const inputDir = path_1.default.resolve(projectRoot, set.inputDir);
|
|
42
|
+
const fontFamily = set.fontFamily ?? path_1.default.basename(inputDir);
|
|
43
|
+
if (!fs_1.default.existsSync(inputDir)) {
|
|
44
|
+
throw new Error(`[react-native-nano-icons] Input directory does not exist: ${inputDir} (from "${set.inputDir}")`);
|
|
45
|
+
}
|
|
46
|
+
const outputDir = set.outputDir
|
|
47
|
+
? path_1.default.resolve(projectRoot, set.outputDir)
|
|
48
|
+
: path_1.default.join(path_1.default.dirname(inputDir), 'nanoicons');
|
|
49
|
+
const ttfPath = path_1.default.join(outputDir, `${fontFamily}.ttf`);
|
|
50
|
+
const glyphmapPath = path_1.default.join(outputDir, `${fontFamily}.glyphmap.json`);
|
|
51
|
+
const inputHash = (0, fingerPrint_js_1.getFingerprintSync)(inputDir);
|
|
52
|
+
if (shouldSkipGeneration(inputHash, outputDir, fontFamily, logger)) {
|
|
53
|
+
results.push({ fontFamily, ttfPath, glyphmapPath });
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (fs_1.default.existsSync(ttfPath))
|
|
57
|
+
fs_1.default.unlinkSync(ttfPath);
|
|
58
|
+
if (fs_1.default.existsSync(glyphmapPath))
|
|
59
|
+
fs_1.default.unlinkSync(glyphmapPath);
|
|
60
|
+
allSkipped = false;
|
|
61
|
+
const tempDir = path_1.default.join(projectRoot, '.temp_layers', fontFamily);
|
|
62
|
+
const config = {
|
|
63
|
+
fontFamily,
|
|
64
|
+
upm: set.upm ?? DEFAULT_UPM,
|
|
65
|
+
safeZone: set.safeZone ?? DEFAULT_SAFE_ZONE,
|
|
66
|
+
startUnicode: set.startUnicode !== undefined
|
|
67
|
+
? typeof set.startUnicode === 'string'
|
|
68
|
+
? parseInt(set.startUnicode, 16)
|
|
69
|
+
: set.startUnicode
|
|
70
|
+
: DEFAULT_START_UNICODE,
|
|
71
|
+
};
|
|
72
|
+
logger?.start(`Building ${fontFamily} (${i + 1}/${iconSets.length})…`);
|
|
73
|
+
const out = await (0, index_js_1.runPipeline)(config, { inputDir, outputDir, tempDir }, { logger, inputHash });
|
|
74
|
+
results.push({
|
|
75
|
+
fontFamily,
|
|
76
|
+
ttfPath: out.ttfPath,
|
|
77
|
+
glyphmapPath: out.glyphmapPath,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (allSkipped && results.length > 0) {
|
|
81
|
+
logger?.succeed('Your icons are flight-tuned with react-native-nano-icons');
|
|
82
|
+
}
|
|
83
|
+
return results;
|
|
84
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IconSetConfig } from './build.js';
|
|
2
|
+
export type NanoIconsConfig = {
|
|
3
|
+
iconSets: IconSetConfig[];
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Load .nanoicons.json from the project root.
|
|
7
|
+
* Throws with a helpful message if the file is missing or malformed.
|
|
8
|
+
*/
|
|
9
|
+
export declare function loadNanoIconsConfig(projectRoot: string): NanoIconsConfig;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadNanoIconsConfig = loadNanoIconsConfig;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
/**
|
|
10
|
+
* Load .nanoicons.json from the project root.
|
|
11
|
+
* Throws with a helpful message if the file is missing or malformed.
|
|
12
|
+
*/
|
|
13
|
+
function loadNanoIconsConfig(projectRoot) {
|
|
14
|
+
const configPath = node_path_1.default.join(projectRoot, '.nanoicons.json');
|
|
15
|
+
if (!node_fs_1.default.existsSync(configPath)) {
|
|
16
|
+
throw new Error(`[react-native-nano-icons] No .nanoicons.json found at project root (${projectRoot}).\n` +
|
|
17
|
+
`Create one with: { "iconSets": [{ "inputDir": "assets/icons", "fontFamily": "MyIcons" }] }`);
|
|
18
|
+
}
|
|
19
|
+
const raw = node_fs_1.default.readFileSync(configPath, 'utf8');
|
|
20
|
+
const config = JSON.parse(raw);
|
|
21
|
+
if (!config?.iconSets?.length) {
|
|
22
|
+
throw new Error(`[react-native-nano-icons] .nanoicons.json must contain an "iconSets" array with at least one entry.`);
|
|
23
|
+
}
|
|
24
|
+
return config;
|
|
25
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { buildAllFonts, type IconSetConfig, type BuiltFont } from './build.js';
|
|
2
|
+
export { createOraLogger, createQuietLogger, detectExpoLogLevel, type NanoLogger, type LogLevel, } from './logger.js';
|
|
3
|
+
export { loadNanoIconsConfig, type NanoIconsConfig } from './config.js';
|
|
4
|
+
export { linkBare } from './link.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.linkBare = exports.loadNanoIconsConfig = exports.detectExpoLogLevel = exports.createQuietLogger = exports.createOraLogger = exports.buildAllFonts = void 0;
|
|
4
|
+
var build_js_1 = require("./build.js");
|
|
5
|
+
Object.defineProperty(exports, "buildAllFonts", { enumerable: true, get: function () { return build_js_1.buildAllFonts; } });
|
|
6
|
+
var logger_js_1 = require("./logger.js");
|
|
7
|
+
Object.defineProperty(exports, "createOraLogger", { enumerable: true, get: function () { return logger_js_1.createOraLogger; } });
|
|
8
|
+
Object.defineProperty(exports, "createQuietLogger", { enumerable: true, get: function () { return logger_js_1.createQuietLogger; } });
|
|
9
|
+
Object.defineProperty(exports, "detectExpoLogLevel", { enumerable: true, get: function () { return logger_js_1.detectExpoLogLevel; } });
|
|
10
|
+
var config_js_1 = require("./config.js");
|
|
11
|
+
Object.defineProperty(exports, "loadNanoIconsConfig", { enumerable: true, get: function () { return config_js_1.loadNanoIconsConfig; } });
|
|
12
|
+
var link_js_1 = require("./link.js");
|
|
13
|
+
Object.defineProperty(exports, "linkBare", { enumerable: true, get: function () { return link_js_1.linkBare; } });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { NanoLogger } from './logger.js';
|
|
2
|
+
import type { BuiltFont } from './build.js';
|
|
3
|
+
/**
|
|
4
|
+
* Link built TTFs into native project directories.
|
|
5
|
+
*
|
|
6
|
+
* Handles three cases:
|
|
7
|
+
* - Both android/ and ios/ exist → link both platforms
|
|
8
|
+
* - Only one platform dir exists → link that platform only
|
|
9
|
+
* - Neither exists (e.g. React Native Web) → skip native linking, report output dir
|
|
10
|
+
*/
|
|
11
|
+
export declare function linkBare(projectRoot: string, builtFonts: BuiltFont[], logger: NanoLogger): Promise<void>;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.linkBare = linkBare;
|
|
40
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
41
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
42
|
+
const plist = __importStar(require("plist"));
|
|
43
|
+
const ANDROID_FONTS_DIR = 'android/app/src/main/assets/fonts';
|
|
44
|
+
const IOS_NANOICONS_FONTS_DIR = 'nanoicons-fonts';
|
|
45
|
+
const IOS_RUN_SCRIPT_PHASE_NAME = 'Copy nanoicons fonts';
|
|
46
|
+
async function linkAndroid(projectRoot, builtFonts) {
|
|
47
|
+
const androidFontsPath = node_path_1.default.join(projectRoot, ANDROID_FONTS_DIR);
|
|
48
|
+
node_fs_1.default.mkdirSync(androidFontsPath, { recursive: true });
|
|
49
|
+
for (const b of builtFonts) {
|
|
50
|
+
const dest = node_path_1.default.join(androidFontsPath, node_path_1.default.basename(b.ttfPath));
|
|
51
|
+
node_fs_1.default.copyFileSync(b.ttfPath, dest);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function linkIos(projectRoot, builtFonts) {
|
|
55
|
+
const iosDir = node_path_1.default.join(projectRoot, 'ios');
|
|
56
|
+
const appDir = node_fs_1.default
|
|
57
|
+
.readdirSync(iosDir, { withFileTypes: true })
|
|
58
|
+
.filter((d) => d.isDirectory())
|
|
59
|
+
.find((d) => node_fs_1.default.existsSync(node_path_1.default.join(iosDir, d.name, 'Info.plist')));
|
|
60
|
+
if (!appDir)
|
|
61
|
+
return;
|
|
62
|
+
const fontNames = [];
|
|
63
|
+
const iosFontsStaging = node_path_1.default.join(iosDir, IOS_NANOICONS_FONTS_DIR);
|
|
64
|
+
node_fs_1.default.mkdirSync(iosFontsStaging, { recursive: true });
|
|
65
|
+
for (const b of builtFonts) {
|
|
66
|
+
const name = node_path_1.default.basename(b.ttfPath);
|
|
67
|
+
fontNames.push(name);
|
|
68
|
+
node_fs_1.default.copyFileSync(b.ttfPath, node_path_1.default.join(iosFontsStaging, name));
|
|
69
|
+
}
|
|
70
|
+
const infoPlistPath = node_path_1.default.join(iosDir, appDir.name, 'Info.plist');
|
|
71
|
+
const plistContent = node_fs_1.default.readFileSync(infoPlistPath, 'utf8');
|
|
72
|
+
const obj = plist.parse(plistContent);
|
|
73
|
+
const existing = Array.isArray(obj['UIAppFonts'])
|
|
74
|
+
? obj['UIAppFonts']
|
|
75
|
+
: [];
|
|
76
|
+
const merged = [...new Set([...existing, ...fontNames])];
|
|
77
|
+
const updated = {
|
|
78
|
+
...obj,
|
|
79
|
+
UIAppFonts: merged,
|
|
80
|
+
};
|
|
81
|
+
node_fs_1.default.writeFileSync(infoPlistPath, plist.build(updated), 'utf8');
|
|
82
|
+
const xcodeprojDir = node_fs_1.default
|
|
83
|
+
.readdirSync(iosDir, { withFileTypes: true })
|
|
84
|
+
.find((d) => d.name.endsWith('.xcodeproj'));
|
|
85
|
+
if (xcodeprojDir) {
|
|
86
|
+
const pbxprojPath = node_path_1.default.join(iosDir, xcodeprojDir.name, 'project.pbxproj');
|
|
87
|
+
const xcode = require('xcode');
|
|
88
|
+
const project = xcode.project(pbxprojPath);
|
|
89
|
+
project.parseSync();
|
|
90
|
+
const hasPhase = Object.entries(project.hash.project.objects['PBXShellScriptBuildPhase'] ?? {}).some(([, v]) => typeof v === 'object' && v?.name?.includes(IOS_RUN_SCRIPT_PHASE_NAME));
|
|
91
|
+
if (!hasPhase) {
|
|
92
|
+
const script = `
|
|
93
|
+
NANOICONS_DIR="\\\${PROJECT_DIR}/${IOS_NANOICONS_FONTS_DIR}"
|
|
94
|
+
if [ -d "$NANOICONS_DIR" ]; then
|
|
95
|
+
cp "$NANOICONS_DIR"/*.ttf "\\\${BUILT_PRODUCTS_DIR}/\\\${UNLOCALIZED_RESOURCES_FOLDER_PATH}/" 2>/dev/null || true
|
|
96
|
+
fi
|
|
97
|
+
`;
|
|
98
|
+
project.addBuildPhase([], 'PBXShellScriptBuildPhase', IOS_RUN_SCRIPT_PHASE_NAME, project.getFirstTarget().uuid, { shellPath: '/bin/sh', shellScript: script });
|
|
99
|
+
node_fs_1.default.writeFileSync(pbxprojPath, project.writeSync(), 'utf8');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Link built TTFs into native project directories.
|
|
105
|
+
*
|
|
106
|
+
* Handles three cases:
|
|
107
|
+
* - Both android/ and ios/ exist → link both platforms
|
|
108
|
+
* - Only one platform dir exists → link that platform only
|
|
109
|
+
* - Neither exists (e.g. React Native Web) → skip native linking, report output dir
|
|
110
|
+
*/
|
|
111
|
+
async function linkBare(projectRoot, builtFonts, logger) {
|
|
112
|
+
if (!builtFonts.length)
|
|
113
|
+
return;
|
|
114
|
+
const hasAndroid = node_fs_1.default.existsSync(node_path_1.default.join(projectRoot, 'android'));
|
|
115
|
+
const hasIos = node_fs_1.default.existsSync(node_path_1.default.join(projectRoot, 'ios'));
|
|
116
|
+
if (!hasAndroid && !hasIos) {
|
|
117
|
+
// React Native Web or other non-native target — just report where fonts landed
|
|
118
|
+
const outputDirs = [
|
|
119
|
+
...new Set(builtFonts.map((b) => node_path_1.default.dirname(b.ttfPath))),
|
|
120
|
+
];
|
|
121
|
+
const rel = node_path_1.default.relative(projectRoot, outputDirs[0] ?? '');
|
|
122
|
+
logger.info(`No native directories found — fonts saved to ${rel}/ (no native dirs, skipping link)`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const linkedPlatforms = [];
|
|
126
|
+
if (hasAndroid) {
|
|
127
|
+
await linkAndroid(projectRoot, builtFonts);
|
|
128
|
+
linkedPlatforms.push('android');
|
|
129
|
+
}
|
|
130
|
+
if (hasIos) {
|
|
131
|
+
await linkIos(projectRoot, builtFonts);
|
|
132
|
+
linkedPlatforms.push('ios');
|
|
133
|
+
}
|
|
134
|
+
logger.succeed(`Linked fonts → ${linkedPlatforms.join(', ')}`);
|
|
135
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type LogLevel = 'normal' | 'verbose';
|
|
2
|
+
export type NanoLogger = {
|
|
3
|
+
start: (msg: string) => void;
|
|
4
|
+
update: (msg: string) => void;
|
|
5
|
+
succeed: (msg: string) => void;
|
|
6
|
+
fail: (msg: string) => void;
|
|
7
|
+
/** Only printed when level is 'verbose'. */
|
|
8
|
+
info: (msg: string) => void;
|
|
9
|
+
warn: (msg: string) => void;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Create an ora spinner-backed logger.
|
|
13
|
+
* ora and chalk are ESM-only so they are loaded via dynamic import;
|
|
14
|
+
* this factory must be awaited once before use.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createOraLogger(level: LogLevel): Promise<NanoLogger>;
|
|
17
|
+
/**
|
|
18
|
+
* Create a plain-text logger suitable for Expo prebuild context.
|
|
19
|
+
* No ora spinner — only success/error lines are printed to avoid
|
|
20
|
+
* disrupting Expo's own output.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createQuietLogger(level: LogLevel): Promise<NanoLogger>;
|
|
23
|
+
/**
|
|
24
|
+
* Infer log level from the Expo CLI environment.
|
|
25
|
+
* Expo uses EXPO_DEBUG=1 for verbose/debug output.
|
|
26
|
+
*/
|
|
27
|
+
export declare function detectExpoLogLevel(): LogLevel;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createOraLogger = createOraLogger;
|
|
4
|
+
exports.createQuietLogger = createQuietLogger;
|
|
5
|
+
exports.detectExpoLogLevel = detectExpoLogLevel;
|
|
6
|
+
const PREFIX = 'react-native-nano-icons';
|
|
7
|
+
/**
|
|
8
|
+
* Create an ora spinner-backed logger.
|
|
9
|
+
* ora and chalk are ESM-only so they are loaded via dynamic import;
|
|
10
|
+
* this factory must be awaited once before use.
|
|
11
|
+
*/
|
|
12
|
+
async function createOraLogger(level) {
|
|
13
|
+
const [{ default: ora }, { default: chalk }] = await Promise.all([
|
|
14
|
+
import('ora'),
|
|
15
|
+
import('chalk'),
|
|
16
|
+
]);
|
|
17
|
+
const spinner = ora({ prefixText: `🔬 ${chalk.dim(PREFIX)}` });
|
|
18
|
+
const dimPrefix = chalk.dim(` ℹ `);
|
|
19
|
+
return {
|
|
20
|
+
start(msg) {
|
|
21
|
+
spinner.start(msg);
|
|
22
|
+
},
|
|
23
|
+
update(msg) {
|
|
24
|
+
spinner.text = msg;
|
|
25
|
+
},
|
|
26
|
+
succeed(msg) {
|
|
27
|
+
spinner.succeed(msg);
|
|
28
|
+
},
|
|
29
|
+
fail(msg) {
|
|
30
|
+
spinner.fail(chalk.red(msg));
|
|
31
|
+
},
|
|
32
|
+
info(msg) {
|
|
33
|
+
if (level === 'verbose') {
|
|
34
|
+
// Print below the current spinner without disrupting it
|
|
35
|
+
process.stdout.write(`\n${dimPrefix}${chalk.dim(msg)}`);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
warn(msg) {
|
|
39
|
+
spinner.warn(chalk.yellow(msg));
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Create a plain-text logger suitable for Expo prebuild context.
|
|
45
|
+
* No ora spinner — only success/error lines are printed to avoid
|
|
46
|
+
* disrupting Expo's own output.
|
|
47
|
+
*/
|
|
48
|
+
async function createQuietLogger(level) {
|
|
49
|
+
const { default: chalk } = await import('chalk');
|
|
50
|
+
const dimPrefix = `🔬 ${chalk.dim(PREFIX)}`;
|
|
51
|
+
const tick = chalk.green('✓');
|
|
52
|
+
const cross = chalk.red('✗');
|
|
53
|
+
const info = chalk.blue('ℹ');
|
|
54
|
+
const warning = chalk.yellow('⚠');
|
|
55
|
+
return {
|
|
56
|
+
start(_msg) {
|
|
57
|
+
/* no-op */
|
|
58
|
+
},
|
|
59
|
+
update(_msg) {
|
|
60
|
+
/* no-op */
|
|
61
|
+
},
|
|
62
|
+
succeed(msg) {
|
|
63
|
+
console.log(`${dimPrefix} ${tick} ${msg}`);
|
|
64
|
+
},
|
|
65
|
+
fail(msg) {
|
|
66
|
+
console.error(`${dimPrefix} ${cross} ${msg}`);
|
|
67
|
+
},
|
|
68
|
+
info(msg) {
|
|
69
|
+
if (level === 'verbose')
|
|
70
|
+
console.log(`${dimPrefix} ${info} ${msg}`);
|
|
71
|
+
},
|
|
72
|
+
warn(msg) {
|
|
73
|
+
console.warn(`${dimPrefix} ${warning} ${msg}`);
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Infer log level from the Expo CLI environment.
|
|
79
|
+
* Expo uses EXPO_DEBUG=1 for verbose/debug output.
|
|
80
|
+
*/
|
|
81
|
+
function detectExpoLogLevel() {
|
|
82
|
+
return process.env['EXPO_DEBUG'] ? 'verbose' : 'normal';
|
|
83
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const createNanoIconSet = (...args) => {
|
|
3
|
+
const { createIconSet } = require('./createNanoIconsSet');
|
|
4
|
+
return createIconSet(...args);
|
|
5
|
+
};
|
|
6
|
+
const plugin = require('./plugin/src/index')
|
|
7
|
+
.default;
|
|
8
|
+
module.exports = Object.assign(plugin, { createNanoIconSet });
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { IconSetConfig, BuiltFont } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build TTF + glyphmap for all icon sets.
|
|
4
|
+
* Shows an ora spinner per font set; catches errors and displays a friendly message
|
|
5
|
+
* unless EXPO_DEBUG is set, in which case the full error is re-thrown.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildAllFonts(iconSets: IconSetConfig[], projectRoot: string): Promise<BuiltFont[]>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildAllFonts = buildAllFonts;
|
|
4
|
+
const index_js_1 = require("../../cli/index.js");
|
|
5
|
+
/**
|
|
6
|
+
* Build TTF + glyphmap for all icon sets.
|
|
7
|
+
* Shows an ora spinner per font set; catches errors and displays a friendly message
|
|
8
|
+
* unless EXPO_DEBUG is set, in which case the full error is re-thrown.
|
|
9
|
+
*/
|
|
10
|
+
async function buildAllFonts(iconSets, projectRoot) {
|
|
11
|
+
const level = (0, index_js_1.detectExpoLogLevel)();
|
|
12
|
+
const logger = await (0, index_js_1.createQuietLogger)(level);
|
|
13
|
+
try {
|
|
14
|
+
return await (0, index_js_1.buildAllFonts)(iconSets, projectRoot, { logger });
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
if (level === 'verbose') {
|
|
18
|
+
throw err;
|
|
19
|
+
}
|
|
20
|
+
logger.fail('Error optimizing icons. Run with EXPO_DEBUG=1 for more logs.');
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
}
|