@fortawesome/react-native-fontawesome 0.3.2 → 1.0.0-alpha.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/package.json CHANGED
@@ -1,78 +1,189 @@
1
1
  {
2
2
  "name": "@fortawesome/react-native-fontawesome",
3
- "version": "0.3.2",
3
+ "version": "1.0.0-alpha.1",
4
4
  "description": "Official React Native component for Font Awesome",
5
- "main": "index.js",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "source": "./src/index.tsx",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "android",
19
+ "ios",
20
+ "cpp",
21
+ "*.podspec",
22
+ "react-native.config.js",
23
+ "!ios/build",
24
+ "!android/build",
25
+ "!android/gradle",
26
+ "!android/gradlew",
27
+ "!android/gradlew.bat",
28
+ "!android/local.properties",
29
+ "!**/__tests__",
30
+ "!**/__fixtures__",
31
+ "!**/__mocks__",
32
+ "!**/.*"
33
+ ],
6
34
  "scripts": {
7
- "test": "jest --forceExit",
8
- "lint": "eslint src",
9
- "dist:watch": "babel --watch --config-file ./babel.dist.config.js src --ignore src/components/__tests__ --out-dir dist",
10
- "dist": "babel --config-file ./babel.dist.config.js src --ignore src/components/__tests__ --ignore src/components/__fixtures__ --out-dir dist",
11
- "set.rnsvg": "yarn upgrade react-native-svg@$RNSVG_VERSION"
35
+ "example": "npm run --workspace=react-native-fontawesome-example",
36
+ "clean": "del-cli lib",
37
+ "prepare": "bob build",
38
+ "typecheck": "tsc",
39
+ "test": "NODE_OPTIONS='--localstorage-file=/tmp/jest-localstorage.db' jest",
40
+ "release": "release-it --only-version",
41
+ "lint": "eslint \"**/*.{js,ts,tsx}\""
12
42
  },
13
- "types": "index.d.ts",
14
- "homepage": "https://github.com/FortAwesome/react-native-fontawesome",
43
+ "keywords": [
44
+ "react-native",
45
+ "ios",
46
+ "android"
47
+ ],
15
48
  "repository": {
16
49
  "type": "git",
17
- "url": "https://github.com/FortAwesome/react-native-fontawesome.git"
50
+ "url": "git+https://github.com/FortAwesome/react-native-fontawesome.git"
18
51
  },
19
- "contributors": [
20
- "Travis Chase <travis@fontawesome.com>",
21
- "Rob Madole <rob@fontawesome.com>",
22
- "Mike Wilkerson <mwilkerson@gmail.com>",
23
- "Dizy <mhisf@vip.qq.com>",
24
- "David Martin <github.com/iamdavidmartin>",
25
- "Jeremy <github.com/puremana>",
26
- "Michael Schonfeld <github.com/schonfeld>",
27
- "Ádám Gólya <github.com/golya>",
28
- "Edward Emanuel <github.com/ej2>",
29
- "Jason Lundien <github.com/jasonlundien>",
30
- "Greg Marut <github.com/gregmarut>"
31
- ],
52
+ "author": "Font Awesome Team <hello@fontawesome.com> (https://fontawesome.com)",
32
53
  "license": "MIT",
33
- "peerDependencies": {
34
- "@fortawesome/fontawesome-svg-core": "~1 || ~6",
35
- "react-native": ">= 0.67",
36
- "react-native-svg": ">= 11.x"
54
+ "bugs": {
55
+ "url": "https://github.com/FortAwesome/react-native-fontawesome/issues"
56
+ },
57
+ "homepage": "https://github.com/FortAwesome/react-native-fontawesome#readme",
58
+ "publishConfig": {
59
+ "registry": "https://registry.npmjs.org/"
37
60
  },
38
61
  "devDependencies": {
39
- "@babel/cli": "^7.7.5",
40
- "@babel/core": "^7.7.5",
41
- "@babel/preset-env": "^7.7.6",
42
- "@babel/preset-react": "^7.7.4",
43
- "@babel/runtime": "^7.7.6",
44
- "@fortawesome/fontawesome-svg-core": "^6",
45
- "babel-core": "^7.0.0-bridge",
46
- "babel-jest": "^24.9.0",
47
- "eslint": "^8.16.0",
48
- "eslint-config-standard": "^17.0.0",
49
- "eslint-plugin-import": "^2.26.0",
50
- "eslint-plugin-jest": "^26.2.2",
51
- "eslint-plugin-n": "^15.2.0",
52
- "eslint-plugin-promise": "^6.0.0",
53
- "eslint-plugin-react": "^7.30.0",
54
- "jest": "^28.1.0",
55
- "metro-react-native-babel-preset": "^0.57.0",
56
- "react": "^17 || ^18",
57
- "react-native": "^0.68.0",
58
- "react-native-svg": "^12.3.0",
59
- "react-test-renderer": "^17"
62
+ "@commitlint/config-conventional": "^19.8.1",
63
+ "@eslint/compat": "^1.3.2",
64
+ "@eslint/eslintrc": "^3.3.1",
65
+ "@eslint/js": "^9.35.0",
66
+ "@fortawesome/fontawesome-svg-core": "^7.0.0",
67
+ "@fortawesome/free-solid-svg-icons": "^7.0.0",
68
+ "@react-native/babel-preset": "0.83.0",
69
+ "@react-native/eslint-config": "0.83.0",
70
+ "@release-it/conventional-changelog": "^10.0.1",
71
+ "@types/humps": "^2.0.6",
72
+ "@types/jest": "^29.5.14",
73
+ "@types/lodash": "^4.17.23",
74
+ "@types/react": "^19.1.12",
75
+ "@types/react-test-renderer": "^19.1.0",
76
+ "commitlint": "^19.8.1",
77
+ "del-cli": "^6.0.0",
78
+ "eslint": "^9.35.0",
79
+ "eslint-config-prettier": "^10.1.8",
80
+ "eslint-plugin-ft-flow": "^3.0.11",
81
+ "eslint-plugin-prettier": "^5.5.4",
82
+ "eslint-plugin-react-native": "^5.0.0",
83
+ "hermes-eslint": "^0.33.3",
84
+ "jest": "^29.7.0",
85
+ "lefthook": "^2.0.3",
86
+ "lodash": "^4.17.23",
87
+ "prettier": "^3.3.3",
88
+ "react": "19.1.0",
89
+ "react-native": "0.81.5",
90
+ "react-native-builder-bob": "^0.40.17",
91
+ "react-native-svg": "^15.15.1",
92
+ "react-test-renderer": "^19.1.0",
93
+ "release-it": "^19.0.4",
94
+ "typescript": "^5.9.2"
60
95
  },
61
- "dependencies": {
62
- "humps": "^2.0.1",
63
- "prop-types": "^15.7.2"
96
+ "peerDependencies": {
97
+ "@fortawesome/fontawesome-svg-core": "~7",
98
+ "react": "*",
99
+ "react-native": "*",
100
+ "react-native-svg": ">=11"
64
101
  },
65
- "files": [
66
- "index.js",
67
- "index.d.ts",
68
- "dist/converter.js",
69
- "dist/logger.js",
70
- "dist/components/FontAwesomeIcon.js"
102
+ "workspaces": [
103
+ "example"
71
104
  ],
105
+ "react-native-builder-bob": {
106
+ "source": "src",
107
+ "output": "lib",
108
+ "targets": [
109
+ [
110
+ "module",
111
+ {
112
+ "esm": true
113
+ }
114
+ ],
115
+ [
116
+ "typescript",
117
+ {
118
+ "project": "tsconfig.build.json"
119
+ }
120
+ ]
121
+ ]
122
+ },
72
123
  "jest": {
73
124
  "preset": "react-native",
125
+ "testEnvironmentOptions": {
126
+ "customExportConditions": [
127
+ "react-native"
128
+ ]
129
+ },
74
130
  "modulePathIgnorePatterns": [
75
- "<rootDir>/examples"
131
+ "<rootDir>/example/node_modules",
132
+ "<rootDir>/lib/"
133
+ ],
134
+ "testPathIgnorePatterns": [
135
+ "<rootDir>/node_modules/",
136
+ "<rootDir>/lib/",
137
+ "<rootDir>/src/__tests__/__fixtures__/",
138
+ "<rootDir>/src/__tests__/__snapshots__/"
139
+ ],
140
+ "fakeTimers": {
141
+ "enableGlobally": false
142
+ }
143
+ },
144
+ "commitlint": {
145
+ "extends": [
146
+ "@commitlint/config-conventional"
76
147
  ]
148
+ },
149
+ "release-it": {
150
+ "git": {
151
+ "commitMessage": "chore: release ${version}",
152
+ "tagName": "v${version}"
153
+ },
154
+ "npm": {
155
+ "publish": true
156
+ },
157
+ "github": {
158
+ "release": false
159
+ },
160
+ "plugins": {
161
+ "@release-it/conventional-changelog": {
162
+ "preset": {
163
+ "name": "angular"
164
+ }
165
+ }
166
+ }
167
+ },
168
+ "prettier": {
169
+ "quoteProps": "consistent",
170
+ "singleQuote": true,
171
+ "tabWidth": 2,
172
+ "trailingComma": "es5",
173
+ "useTabs": false
174
+ },
175
+ "create-react-native-library": {
176
+ "type": "library",
177
+ "languages": "js",
178
+ "tools": [
179
+ "jest",
180
+ "lefthook",
181
+ "release-it",
182
+ "eslint"
183
+ ],
184
+ "version": "0.57.0"
185
+ },
186
+ "dependencies": {
187
+ "humps": "^2.0.1"
77
188
  }
78
189
  }
@@ -0,0 +1,331 @@
1
+ import React from 'react';
2
+ import convert from './converter';
3
+ import { StyleSheet } from 'react-native';
4
+ import type { StyleProp, ViewStyle } from 'react-native';
5
+ import { icon, parse } from '@fortawesome/fontawesome-svg-core';
6
+ import type {
7
+ Transform,
8
+ IconProp,
9
+ IconPrefix,
10
+ IconName,
11
+ IconLookup as FAIconLookup,
12
+ IconDefinition as FAIconDefinition,
13
+ } from '@fortawesome/fontawesome-svg-core';
14
+ import log from './logger';
15
+
16
+ export const DEFAULT_SIZE = 16;
17
+ export const DEFAULT_COLOR = '#000';
18
+ export const DEFAULT_SECONDARY_OPACITY = 0.4;
19
+
20
+ export type FontAwesomeIconStyle = StyleProp<ViewStyle> & {
21
+ color?: string;
22
+ };
23
+
24
+ export interface Props {
25
+ icon: IconProp;
26
+ /** @deprecated Use size instead */
27
+ height?: number;
28
+ /** @deprecated Use size instead */
29
+ width?: number;
30
+ size?: number;
31
+ color?: string;
32
+ secondaryColor?: string;
33
+ secondaryOpacity?: number;
34
+ mask?: IconProp;
35
+ maskId?: string;
36
+ transform?: string | Transform;
37
+ style?: FontAwesomeIconStyle;
38
+ testID?: string;
39
+ }
40
+
41
+ type IconLookupOrDefinition = FAIconLookup | FAIconDefinition;
42
+
43
+ interface AbstractElement {
44
+ tag: string;
45
+ attributes: Record<string, unknown>;
46
+ children?: (AbstractElement | string)[];
47
+ }
48
+
49
+ function objectWithKey(
50
+ key: string,
51
+ value: unknown
52
+ ): Record<string, unknown> | Record<string, never> {
53
+ return (Array.isArray(value) && value.length > 0) ||
54
+ (!Array.isArray(value) && value)
55
+ ? { [key]: value }
56
+ : {};
57
+ }
58
+
59
+ function normalizeIconArgs(
60
+ iconArg: IconProp | null | undefined
61
+ ): IconLookupOrDefinition | null {
62
+ // If it's a full icon definition object (with prefix, iconName, and icon array),
63
+ // return it as-is so icon() can use it directly
64
+ if (
65
+ iconArg &&
66
+ typeof iconArg === 'object' &&
67
+ 'prefix' in iconArg &&
68
+ 'iconName' in iconArg &&
69
+ 'icon' in iconArg
70
+ ) {
71
+ return iconArg as FAIconDefinition;
72
+ }
73
+
74
+ if ((parse as { icon?: (icon: IconProp) => FAIconLookup }).icon) {
75
+ return (parse as { icon: (icon: IconProp) => FAIconLookup }).icon(
76
+ iconArg as IconProp
77
+ );
78
+ }
79
+
80
+ if (iconArg === null) {
81
+ return null;
82
+ }
83
+
84
+ if (Array.isArray(iconArg) && iconArg.length === 2) {
85
+ return {
86
+ prefix: iconArg[0] as IconPrefix,
87
+ iconName: iconArg[1] as IconName,
88
+ };
89
+ }
90
+
91
+ if (typeof iconArg === 'string') {
92
+ return { prefix: 'fas' as IconPrefix, iconName: iconArg as IconName };
93
+ }
94
+
95
+ return null;
96
+ }
97
+
98
+ export default function FontAwesomeIcon(
99
+ props: Props
100
+ ): React.ReactElement | null {
101
+ const {
102
+ icon: iconArgs,
103
+ mask: maskArgs,
104
+ maskId = null,
105
+ height,
106
+ width,
107
+ size = DEFAULT_SIZE,
108
+ style: styleInput = {},
109
+ color: colorInput = null,
110
+ secondaryColor: secondaryColorInput = null,
111
+ secondaryOpacity: secondaryOpacityInput = null,
112
+ transform: transformInput = null,
113
+ } = props;
114
+ const style = StyleSheet.flatten(styleInput) as Record<
115
+ string,
116
+ unknown
117
+ > | null;
118
+
119
+ const iconLookup = normalizeIconArgs(iconArgs);
120
+ const transform = objectWithKey(
121
+ 'transform',
122
+ typeof transformInput === 'string'
123
+ ? parse.transform(transformInput)
124
+ : transformInput
125
+ );
126
+ const mask = objectWithKey('mask', normalizeIconArgs(maskArgs));
127
+
128
+ const renderedIcon = icon(iconLookup as IconLookupOrDefinition, {
129
+ ...transform,
130
+ ...mask,
131
+ maskId: maskId ?? undefined,
132
+ });
133
+
134
+ if (!renderedIcon) {
135
+ log('ERROR: icon not found for icon = ', iconArgs);
136
+ return null;
137
+ }
138
+
139
+ const { abstract } = renderedIcon;
140
+
141
+ // This is the color that will be passed to the "fill" prop of the Svg element
142
+ const color = colorInput || (style || {}).color || DEFAULT_COLOR;
143
+
144
+ // This is the color that will be passed to the "fill" prop of the secondary Path element child (in Duotone Icons)
145
+ // `null` value will result in using the primary color, at 40% opacity
146
+ const secondaryColor = secondaryColorInput || color;
147
+
148
+ // Secondary layer opacity should default to 0.4, unless a specific opacity value or a specific secondary color was given
149
+ const secondaryOpacity = secondaryOpacityInput || DEFAULT_SECONDARY_OPACITY;
150
+
151
+ // To avoid confusion down the line, we'll remove properties from the StyleSheet, like color, that are being overridden
152
+ // or resolved in other ways, to avoid ambiguity as to which inputs cause which outputs in the underlying rendering process.
153
+ // In other words, we don't want color (for example) to be specified via two different inputs.
154
+ // Intentionally extract and discard color from style to avoid ambiguity
155
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
156
+ const { color: _styleColor, ...modifiedStyle } = style || {};
157
+
158
+ let resolvedHeight: number;
159
+ let resolvedWidth: number;
160
+
161
+ if (height || width) {
162
+ throw new Error(
163
+ `Prop height and width for component ${FontAwesomeIcon.displayName} have been deprecated. ` +
164
+ `Use the size prop instead like <${FontAwesomeIcon.displayName} size={${width}} />.`
165
+ );
166
+ } else {
167
+ resolvedHeight = resolvedWidth = size || DEFAULT_SIZE;
168
+ }
169
+
170
+ const rootAttributes = (abstract[0] as AbstractElement).attributes;
171
+
172
+ rootAttributes.height = resolvedHeight;
173
+ rootAttributes.width = resolvedWidth;
174
+ rootAttributes.style = modifiedStyle;
175
+
176
+ replaceCurrentColor(
177
+ abstract[0] as AbstractElement,
178
+ color as string,
179
+ secondaryColor as string,
180
+ secondaryOpacity
181
+ );
182
+
183
+ // Expand viewBox for FA7 overflow icon support
184
+ // This must happen before percentage replacement to ensure correct dimensions
185
+ if (rootAttributes.viewBox) {
186
+ rootAttributes.viewBox = expandViewBox(rootAttributes.viewBox as string);
187
+ }
188
+
189
+ // Parse viewBox to get dimensions for percentage replacement
190
+ // viewBox format: "minX minY width height" e.g., "0 -32 512 576" (after expansion)
191
+ const viewBox = rootAttributes.viewBox as string | undefined;
192
+ if (viewBox) {
193
+ const parts = viewBox.split(' ').map(Number);
194
+ if (parts.length === 4) {
195
+ const vbWidth = parts[2];
196
+ const vbHeight = parts[3];
197
+ if (vbWidth !== undefined && vbHeight !== undefined) {
198
+ replacePercentages(abstract[0] as AbstractElement, vbWidth, vbHeight);
199
+ }
200
+ }
201
+ }
202
+
203
+ // AbstractElement input always produces a ReactElement (not string), so cast is safe
204
+ return convertCurry(abstract[0] as AbstractElement) as React.ReactElement;
205
+ }
206
+
207
+ FontAwesomeIcon.displayName = 'FontAwesomeIcon';
208
+
209
+ const convertCurry = convert.bind(null, React.createElement);
210
+
211
+ function replaceCurrentColor(
212
+ obj: AbstractElement,
213
+ primaryColor: string,
214
+ secondaryColor: string,
215
+ secondaryOpacity: number
216
+ ): void {
217
+ (obj.children || []).forEach((child) => {
218
+ if (typeof child === 'string') return;
219
+
220
+ replaceFill(child, primaryColor, secondaryColor, secondaryOpacity);
221
+
222
+ if (Object.prototype.hasOwnProperty.call(child, 'attributes')) {
223
+ replaceFill(
224
+ child.attributes,
225
+ primaryColor,
226
+ secondaryColor,
227
+ secondaryOpacity
228
+ );
229
+ }
230
+
231
+ if (Array.isArray(child.children) && child.children.length > 0) {
232
+ replaceCurrentColor(
233
+ child,
234
+ primaryColor,
235
+ secondaryColor,
236
+ secondaryOpacity
237
+ );
238
+ }
239
+ });
240
+ }
241
+
242
+ function replaceFill(
243
+ obj: Record<string, unknown> | AbstractElement,
244
+ primaryColor: string,
245
+ secondaryColor: string,
246
+ secondaryOpacity: number
247
+ ): void {
248
+ if (hasPropertySetToValue(obj, 'fill', 'currentColor')) {
249
+ if (hasPropertySetToValue(obj, 'class', 'fa-primary')) {
250
+ (obj as Record<string, unknown>).fill = primaryColor;
251
+ } else if (hasPropertySetToValue(obj, 'class', 'fa-secondary')) {
252
+ (obj as Record<string, unknown>).fill = secondaryColor;
253
+ (obj as Record<string, unknown>).fillOpacity = secondaryOpacity;
254
+ } else {
255
+ (obj as Record<string, unknown>).fill = primaryColor;
256
+ }
257
+ }
258
+ }
259
+
260
+ function hasPropertySetToValue(
261
+ obj: Record<string, unknown> | AbstractElement,
262
+ property: string,
263
+ value: unknown
264
+ ): boolean {
265
+ return (
266
+ Object.prototype.hasOwnProperty.call(obj, property) &&
267
+ (obj as Record<string, unknown>)[property] === value
268
+ );
269
+ }
270
+
271
+ /**
272
+ * Expands the viewBox to accommodate Font Awesome 7 icons that overflow their standard viewBox.
273
+ * FA7 introduced icons where paths extend beyond the declared viewBox boundaries.
274
+ * React Native clips content to the viewBox (unlike web browsers which support CSS overflow: visible).
275
+ * This expansion adds vertical space by subtracting 32 from minY and adding 64 to height.
276
+ *
277
+ * @param viewBox - The original viewBox string in format "minX minY width height"
278
+ * @returns The expanded viewBox string with adjusted minY and height
279
+ */
280
+ export function expandViewBox(viewBox: string): string {
281
+ const parts = viewBox.split(' ').map(Number);
282
+ if (parts.length !== 4) return viewBox;
283
+
284
+ const minX = parts[0];
285
+ const minY = parts[1];
286
+ const width = parts[2];
287
+ const height = parts[3];
288
+
289
+ // Validate that all parts are valid numbers
290
+ if (
291
+ minX === undefined ||
292
+ minY === undefined ||
293
+ width === undefined ||
294
+ height === undefined ||
295
+ isNaN(minX) ||
296
+ isNaN(minY) ||
297
+ isNaN(width) ||
298
+ isNaN(height)
299
+ ) {
300
+ return viewBox;
301
+ }
302
+
303
+ // Expand for FA7 overflow icons: subtract 32 from minY, add 64 to height
304
+ return `${minX} ${minY - 32} ${width} ${height + 64}`;
305
+ }
306
+
307
+ /**
308
+ * react-native-svg has issues with percentage values like "100%" in masks and rects.
309
+ * This function replaces percentage values with actual numeric values based on the viewBox.
310
+ */
311
+ function replacePercentages(
312
+ obj: AbstractElement,
313
+ viewBoxWidth: number,
314
+ viewBoxHeight: number
315
+ ): void {
316
+ const attrs = obj.attributes as Record<string, unknown> | undefined;
317
+ if (attrs) {
318
+ if (attrs.width === '100%') {
319
+ attrs.width = viewBoxWidth;
320
+ }
321
+ if (attrs.height === '100%') {
322
+ attrs.height = viewBoxHeight;
323
+ }
324
+ }
325
+
326
+ (obj.children || []).forEach((child) => {
327
+ if (typeof child !== 'string') {
328
+ replacePercentages(child, viewBoxWidth, viewBoxHeight);
329
+ }
330
+ });
331
+ }
@@ -0,0 +1,71 @@
1
+ import type React from 'react';
2
+ import humps from 'humps';
3
+ import { Svg, Path, Rect, Defs, Mask, G, ClipPath } from 'react-native-svg';
4
+
5
+ interface AbstractElement {
6
+ tag: string;
7
+ attributes?: Record<string, unknown>;
8
+ children?: (AbstractElement | string)[];
9
+ }
10
+
11
+ type CreateElementFn = typeof import('react').createElement;
12
+
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- required for dynamic component lookup from FA abstract elements
14
+ const svgObjectMap: Record<string, React.ComponentType<any>> = {
15
+ svg: Svg,
16
+ path: Path,
17
+ rect: Rect,
18
+ defs: Defs,
19
+ mask: Mask,
20
+ g: G,
21
+ clipPath: ClipPath,
22
+ };
23
+
24
+ function convert(
25
+ createElement: CreateElementFn,
26
+ element: AbstractElement | string
27
+ ): React.ReactNode {
28
+ if (typeof element === 'string') {
29
+ return element;
30
+ }
31
+
32
+ const children = (element.children || []).map((child) => {
33
+ return convert(createElement, child);
34
+ });
35
+
36
+ const mixins = Object.keys(element.attributes || {}).reduce(
37
+ (acc, key) => {
38
+ const val = (element.attributes as Record<string, unknown>)[key];
39
+ switch (key) {
40
+ case 'class':
41
+ case 'role':
42
+ case 'xmlns':
43
+ delete (element.attributes as Record<string, unknown>)[key];
44
+ break;
45
+ case 'focusable':
46
+ acc.attrs[key] = val === 'true';
47
+ break;
48
+ default:
49
+ if (
50
+ key.indexOf('aria-') === 0 ||
51
+ key.indexOf('data-') === 0 ||
52
+ (key === 'fill' && val === 'currentColor')
53
+ ) {
54
+ delete (element.attributes as Record<string, unknown>)[key];
55
+ } else {
56
+ acc.attrs[humps.camelize(key)] = val;
57
+ }
58
+ }
59
+ return acc;
60
+ },
61
+ { attrs: {} as Record<string, unknown> }
62
+ );
63
+
64
+ return createElement(
65
+ svgObjectMap[element.tag] as React.ComponentType,
66
+ { ...mixins.attrs },
67
+ ...children
68
+ );
69
+ }
70
+
71
+ export default convert;
package/src/index.tsx ADDED
@@ -0,0 +1,7 @@
1
+ export { default as FontAwesomeIcon } from './FontAwesomeIcon';
2
+ export {
3
+ DEFAULT_SIZE,
4
+ DEFAULT_COLOR,
5
+ DEFAULT_SECONDARY_OPACITY,
6
+ } from './FontAwesomeIcon';
7
+ export type { Props, FontAwesomeIconStyle } from './FontAwesomeIcon';
package/src/logger.ts ADDED
@@ -0,0 +1,7 @@
1
+ const PRODUCTION = false;
2
+
3
+ export default function log(...args: unknown[]): void {
4
+ if (!PRODUCTION && console && typeof console.error === 'function') {
5
+ console.error(...args);
6
+ }
7
+ }