react-native-vector-image 0.5.3 → 0.6.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 CHANGED
@@ -8,6 +8,7 @@
8
8
  - Faster render – ~5x faster than `react-native-svg`.
9
9
  - Smaller JS bundle = faster startup.
10
10
  - Native support for dark mode.
11
+ - Web support for Expo.
11
12
 
12
13
  ## Installation
13
14
 
@@ -15,8 +16,6 @@
15
16
  yarn add react-native-vector-image
16
17
  ```
17
18
 
18
- For expo, see [`@zamplyy/react-native-vector-image-plugin`](https://github.com/zamplyy/react-native-vector-image-plugin).
19
-
20
19
  ### Android
21
20
 
22
21
  Edit `android/app/build.gradle` to look like this (without the +):
@@ -44,6 +43,44 @@ REACT_NATIVE_XCODE="$REACT_NATIVE_PATH/scripts/react-native-xcode.sh"
44
43
 
45
44
  <img width="1212" alt="" src="https://user-images.githubusercontent.com/378279/115999935-544c0600-a5ee-11eb-9c59-6fb50e434ed0.png">
46
45
 
46
+ ### Expo
47
+
48
+ After installing this npm package, add the [config plugin](https://docs.expo.io/guides/config-plugins/) to the [`plugins`](https://docs.expo.io/versions/latest/config/app/#plugins) array of your `app.json` or `app.config.js`:
49
+
50
+ ```json
51
+ {
52
+ "expo": {
53
+ "plugins": ["react-native-vector-image"]
54
+ }
55
+ }
56
+ ```
57
+
58
+ Next, rebuild your app as described in the ["Adding custom native code"](https://docs.expo.io/workflow/customizing/) guide.
59
+
60
+ #### Options
61
+
62
+ ```json
63
+ {
64
+ "expo": {
65
+ "plugins": [
66
+ [
67
+ "react-native-vector-image",
68
+ {
69
+ // These are default options, change if you want a different value:
70
+ "stripSvgs": false, // if true, svgs will be removed from bundle. expo-updates package crashes when svgs it expects in the bundle are not there
71
+ "metroConfigFile": "metro.config.js",
72
+ "resetCache": false,
73
+ "bundleWithExpo": true,
74
+ "entryFile": "index.ts"
75
+ }
76
+ ]
77
+ ]
78
+ }
79
+ }
80
+ ```
81
+
82
+ Shout out to [zamplyy](https://github.com/zamplyy) for making the first version of the Expo plugin.
83
+
47
84
  ## Usage
48
85
 
49
86
  Since native vector assets cannot be served over http via metro dev server, they must be generated and compiled into the app bundle.
@@ -62,21 +99,24 @@ To add dark mode to your image, create a new file with an `.dark.svg` extension,
62
99
 
63
100
  This takes a while as metro has to go through all the code to find the imported SVGs.
64
101
 
102
+ _Note: for Expo just use the plugin and `npx expo prebuild`._
103
+
65
104
  ```sh
66
105
  yarn react-native-vector-image generate
67
106
  ```
68
107
 
69
- | Argument | Description | Default |
70
- | ---------------------- | ---------------------------------------------------------------- | ----------------------------- |
71
- | `--entry-file` | Path to the app entrypoint file. | `index.js` |
72
- | `--config` | Path to the metro config file. | `metro.config.js` |
73
- | `--reset-cache` | Reset metro cache before extracting SVG assets. | `false` |
74
- | `--ios-output` | Path to an iOS `.xcassets` folder. | `ios/AppName/Images.xcassets` |
75
- | `--no-ios-output` | Disable iOS output. | `false` |
76
- | `--android-output` | Path to an Android `res` folder. | `android/app/src/main/res` |
77
- | `--no-android-output` | Disable Android output. | `false` |
78
- | `--current-color` | Replace any `currentColor` color references in SVGs. | `#000000` |
79
- | `--current-color-dark` | Replace any `currentColor` color references in `.dark.svg` SVGs. | `#ffffff` |
108
+ | Argument | Description | Default |
109
+ | ---------------------- | -------------------------------------------------------------------- | ----------------------------- |
110
+ | `--entry-file` | Path to the app entrypoint file. | `index.js` |
111
+ | `--config` | Path to the metro config file. | `metro.config.js` |
112
+ | `--reset-cache` | Reset metro cache before extracting SVG assets. | `false` |
113
+ | `--bundle-with-expo` | Whether to bundle the app with Expo presets or using metro directly. | `false` |
114
+ | `--ios-output` | Path to an iOS `.xcassets` folder. | `ios/AppName/Images.xcassets` |
115
+ | `--no-ios-output` | Disable iOS output. | `false` |
116
+ | `--android-output` | Path to an Android `res` folder. | `android/app/src/main/res` |
117
+ | `--no-android-output` | Disable Android output. | `false` |
118
+ | `--current-color` | Replace any `currentColor` color references in SVGs. | `#000000` |
119
+ | `--current-color-dark` | Replace any `currentColor` color references in `.dark.svg` SVGs. | `#ffffff` |
80
120
 
81
121
  ### Step 3: recompile
82
122
 
package/app.plugin.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./plugin/build/withVectorImage');
@@ -0,0 +1,3 @@
1
+ {
2
+ "platforms": ["apple", "android", "web"]
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-vector-image",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "description": "Native vector images generated from SVG",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -10,7 +10,11 @@
10
10
  "url": "git+https://github.com/oblador/react-native-vector-image.git"
11
11
  },
12
12
  "scripts": {
13
- "test": "jest src"
13
+ "test": "jest src",
14
+ "build:plugin": "expo-module build plugin",
15
+ "test:plugin": "expo-module test plugin",
16
+ "prepare": "expo-module prepare plugin",
17
+ "prepublishOnly": "expo-module prepublishOnly plugin"
14
18
  },
15
19
  "bin": {
16
20
  "react-native-vector-image": "bin/react-native-vector-image.js"
@@ -18,6 +22,9 @@
18
22
  "files": [
19
23
  "bin",
20
24
  "src",
25
+ "app.plugin.js",
26
+ "expo-module.config.json",
27
+ "plugin/build",
21
28
  "strip_svgs.sh",
22
29
  "strip_svgs.gradle",
23
30
  "!*.spec.js",
@@ -51,7 +58,10 @@
51
58
  }
52
59
  },
53
60
  "devDependencies": {
54
- "jest": "^26.6.3",
61
+ "expo-module-scripts": "^5.0.7",
62
+ "expo": "^54.0.12",
63
+ "react-native": "0.81.4",
64
+ "jest": "^29.6.3",
55
65
  "metro": ">=0.83.3",
56
66
  "metro-config": ">=0.83.3",
57
67
  "prettier": "^2.2.1"
@@ -0,0 +1 @@
1
+ {"root":["../src/withVectorImage.ts"],"version":"5.9.3"}
@@ -0,0 +1,27 @@
1
+ import { ConfigPlugin, ExportedConfigWithProps, XcodeProject } from 'expo/config-plugins';
2
+ export declare enum GenerateCommands {
3
+ EntryFile = "--entry-file",
4
+ Config = "--config",
5
+ ResetCache = "--reset-cache",
6
+ BundleWithExpo = "--bundle-with-expo"
7
+ }
8
+ export declare const addStripSvgsImplementation: (projectBuildGradle: string) => string;
9
+ export declare const setPBXShellScriptBuildPhaseStripSvg: (config: ExportedConfigWithProps<XcodeProject>) => Promise<ExportedConfigWithProps<XcodeProject>>;
10
+ export declare const getCommands: (commands?: {
11
+ command: GenerateCommands;
12
+ input: string;
13
+ }[]) => string;
14
+ export declare const withGenerateIosAssets: ConfigPlugin<string | void>;
15
+ export declare const withGenerateAndroidAssets: ConfigPlugin<string | void>;
16
+ type VectorImagePlugin = ConfigPlugin<{
17
+ customMetroConfigFile?: string;
18
+ resetCache?: boolean;
19
+ customEntryFile?: string;
20
+ stripSvgs?: boolean;
21
+ bundleWithExpo?: boolean;
22
+ } | void>;
23
+ /**
24
+ * Apply VectorImage configuration for Expo projects.
25
+ */
26
+ declare const withVectorImage: VectorImagePlugin;
27
+ export default withVectorImage;
@@ -0,0 +1,149 @@
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.withGenerateAndroidAssets = exports.withGenerateIosAssets = exports.getCommands = exports.setPBXShellScriptBuildPhaseStripSvg = exports.addStripSvgsImplementation = exports.GenerateCommands = void 0;
7
+ const config_plugins_1 = require("expo/config-plugins");
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ var GenerateCommands;
10
+ (function (GenerateCommands) {
11
+ GenerateCommands["EntryFile"] = "--entry-file";
12
+ GenerateCommands["Config"] = "--config";
13
+ GenerateCommands["ResetCache"] = "--reset-cache";
14
+ GenerateCommands["BundleWithExpo"] = "--bundle-with-expo";
15
+ })(GenerateCommands || (exports.GenerateCommands = GenerateCommands = {}));
16
+ const DefaultCommands = [
17
+ {
18
+ command: GenerateCommands.EntryFile,
19
+ input: 'index.ts',
20
+ },
21
+ {
22
+ command: GenerateCommands.BundleWithExpo,
23
+ input: 'true',
24
+ },
25
+ ];
26
+ const addStripSvgsImplementation = (projectBuildGradle) => {
27
+ const addString = `apply from: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../../react-native-vector-image/strip_svgs.gradle")`;
28
+ const searchString = /"..\/react.gradle"\)\n/gm;
29
+ if (projectBuildGradle.includes(addString)) {
30
+ return projectBuildGradle;
31
+ }
32
+ return projectBuildGradle.replace(searchString, `"../react.gradle")\n${addString}`);
33
+ };
34
+ exports.addStripSvgsImplementation = addStripSvgsImplementation;
35
+ const withStripSvgsAndroid = (config) => {
36
+ return (0, config_plugins_1.withAppBuildGradle)(config, ({ modResults, ...config }) => {
37
+ modResults.contents = (0, exports.addStripSvgsImplementation)(modResults.contents);
38
+ return { modResults, ...config };
39
+ });
40
+ };
41
+ const addSlashes = (str) => {
42
+ return str.replace(/[\\"]/g, '\\$&').replace(/\u0000/g, '\\0');
43
+ };
44
+ const setPBXShellScriptBuildPhaseStripSvg = async (config) => {
45
+ const xcodeProject = config.modResults;
46
+ const pbxShellScriptBuildPhases = xcodeProject.hash.project.objects.PBXShellScriptBuildPhase;
47
+ for (const buildPhase of Object.values(pbxShellScriptBuildPhases)) {
48
+ if (buildPhase?.name === '"Bundle React Native code and images"' &&
49
+ !buildPhase?.shellScript.includes('"react-native-vector-image"')) {
50
+ buildPhase.shellScript =
51
+ buildPhase.shellScript.substring(0, buildPhase.shellScript.length - 1) +
52
+ addSlashes(`\n\`"$NODE_BINARY" --print "require('path').dirname(require.resolve('react-native-vector-image/package.json')) + '/strip_svgs.sh'"\``) +
53
+ '"';
54
+ }
55
+ }
56
+ return config;
57
+ };
58
+ exports.setPBXShellScriptBuildPhaseStripSvg = setPBXShellScriptBuildPhaseStripSvg;
59
+ const withStripSvgsIos = (c) => {
60
+ return (0, config_plugins_1.withXcodeProject)(c, async (config) => {
61
+ config = await (0, exports.setPBXShellScriptBuildPhaseStripSvg)(config);
62
+ return config;
63
+ });
64
+ };
65
+ const getCliPath = () => {
66
+ return node_path_1.default.join(node_path_1.default.dirname(node_path_1.default.dirname(__dirname)), 'src/cli/index');
67
+ };
68
+ const getCommands = (commands = DefaultCommands) => {
69
+ const commandsMap = new Map();
70
+ commands.forEach((c) => commandsMap.set(c.command, c.input));
71
+ DefaultCommands.forEach((c) => {
72
+ const exists = commandsMap.has(c.command);
73
+ if (!exists) {
74
+ commandsMap.set(c.command, c.input);
75
+ }
76
+ });
77
+ return Array.from(commandsMap)
78
+ .map((arr) => `${arr[0]} ${arr[1]}`)
79
+ .join(' ');
80
+ };
81
+ exports.getCommands = getCommands;
82
+ const runCli = (cliCommand) => {
83
+ const cliPath = getCliPath();
84
+ const cli = require(cliPath);
85
+ if (!cli) {
86
+ throw new Error('Could not find react-native-vector-image cli');
87
+ }
88
+ cli(cliCommand);
89
+ };
90
+ const withGenerateIosAssets = (config, commands) => {
91
+ return (0, config_plugins_1.withDangerousMod)(config, [
92
+ 'ios',
93
+ (config) => {
94
+ const appName = config.modRequest.projectName;
95
+ runCli(`generate --no-android-output --ios-output ios/${appName}/Images.xcassets ${commands}`);
96
+ return config;
97
+ },
98
+ ]);
99
+ };
100
+ exports.withGenerateIosAssets = withGenerateIosAssets;
101
+ const withGenerateAndroidAssets = (config, commands) => {
102
+ return (0, config_plugins_1.withDangerousMod)(config, [
103
+ 'android',
104
+ (config) => {
105
+ runCli(`generate --no-ios-output ${commands}`);
106
+ return config;
107
+ },
108
+ ]);
109
+ };
110
+ exports.withGenerateAndroidAssets = withGenerateAndroidAssets;
111
+ /**
112
+ * Apply VectorImage configuration for Expo projects.
113
+ */
114
+ const withVectorImage = (config, props = {}) => {
115
+ const commands = [];
116
+ if (props?.customEntryFile) {
117
+ commands.push({
118
+ command: GenerateCommands.EntryFile,
119
+ input: props?.customEntryFile,
120
+ });
121
+ }
122
+ if (props?.customMetroConfigFile) {
123
+ commands.push({
124
+ command: GenerateCommands.Config,
125
+ input: props?.customMetroConfigFile,
126
+ });
127
+ }
128
+ if (props?.resetCache) {
129
+ commands.push({
130
+ command: GenerateCommands.ResetCache,
131
+ input: `${props?.resetCache}`,
132
+ });
133
+ }
134
+ if (props?.bundleWithExpo) {
135
+ commands.push({
136
+ command: GenerateCommands.BundleWithExpo,
137
+ input: `${props?.bundleWithExpo}`,
138
+ });
139
+ }
140
+ const commandsWithDefault = (0, exports.getCommands)(commands);
141
+ if (props?.stripSvgs) {
142
+ config = withStripSvgsIos(config);
143
+ config = withStripSvgsAndroid(config);
144
+ }
145
+ config = (0, exports.withGenerateIosAssets)(config, commandsWithDefault);
146
+ config = (0, exports.withGenerateAndroidAssets)(config, commandsWithDefault);
147
+ return config;
148
+ };
149
+ exports.default = withVectorImage;
@@ -26,13 +26,17 @@ async function removeGeneratedAssets(directory) {
26
26
  async function generateAssets({
27
27
  iosOutput,
28
28
  androidOutput,
29
+ bundleWithExpo,
29
30
  config,
30
31
  entryFile,
31
32
  resetCache,
32
33
  currentColor,
33
34
  currentColorDark,
34
35
  }) {
35
- const assets = await getAssets({ config, entryFile, resetCache });
36
+ const assets = await getAssets(
37
+ { config, entryFile, resetCache },
38
+ bundleWithExpo
39
+ );
36
40
 
37
41
  if (iosOutput) {
38
42
  await removeGeneratedAssets(iosOutput);
@@ -8,17 +8,15 @@ const getBabelTransformerPath = () => {
8
8
  // for RN 73+
9
9
  return require.resolve('@react-native/metro-babel-transformer');
10
10
  } catch (e) {
11
- try {
12
- // for Expo
13
- return require.resolve('@expo/metro-config/babel-transformer');
14
- } catch (e) {
15
- // to ensure backwards compatibility with old RN versions (RN < 73)
16
- return require.resolve('metro-react-native-babel-transformer');
17
- }
11
+ // to ensure backwards compatibility with old RN versions (RN < 73)
12
+ return require.resolve('metro-react-native-babel-transformer');
18
13
  }
19
14
  };
20
15
 
21
- async function getAssets(options) {
16
+ async function createMetroServerAndBundleRequest(
17
+ options,
18
+ bundleWithExpo = false
19
+ ) {
22
20
  const args = {
23
21
  entryFile: options.entryFile,
24
22
  dev: false,
@@ -26,6 +24,21 @@ async function getAssets(options) {
26
24
  platform: 'ios',
27
25
  };
28
26
 
27
+ if (bundleWithExpo) {
28
+ let createMetroServerAndBundleRequestAsync = null;
29
+ try {
30
+ createMetroServerAndBundleRequestAsync = require('expo/node_modules/@expo/cli/build/src/export/embed/exportEmbedAsync')
31
+ .createMetroServerAndBundleRequestAsync;
32
+ } catch {
33
+ createMetroServerAndBundleRequestAsync = require('@expo/cli/build/src/export/embed/exportEmbedAsync')
34
+ .createMetroServerAndBundleRequestAsync;
35
+ }
36
+ return await createMetroServerAndBundleRequestAsync(process.cwd(), {
37
+ ...args,
38
+ ...options,
39
+ });
40
+ }
41
+
29
42
  const config = await loadConfig(
30
43
  {
31
44
  cwd: process.cwd(),
@@ -46,7 +59,7 @@ async function getAssets(options) {
46
59
 
47
60
  process.env.NODE_ENV = 'production';
48
61
 
49
- const requestOpts = {
62
+ const bundleRequest = {
50
63
  entryFile: args.entryFile,
51
64
  dev: args.dev,
52
65
  minify: false,
@@ -55,12 +68,21 @@ async function getAssets(options) {
55
68
  };
56
69
  const server = new Server(config);
57
70
 
71
+ return { server, bundleRequest };
72
+ }
73
+
74
+ async function getAssets(options, bundleWithExpo) {
75
+ const { server, bundleRequest } = await createMetroServerAndBundleRequest(
76
+ options,
77
+ bundleWithExpo
78
+ );
79
+
58
80
  try {
59
- await output.build(server, requestOpts);
81
+ await output.build(server, bundleRequest);
60
82
 
61
83
  return await server.getAssets({
62
84
  ...Server.DEFAULT_BUNDLE_OPTIONS,
63
- ...requestOpts,
85
+ ...bundleRequest,
64
86
  bundleType: 'todo',
65
87
  });
66
88
  } finally {
package/src/cli/index.js CHANGED
@@ -27,6 +27,13 @@ function main(argv) {
27
27
  demandOption: true,
28
28
  type: 'string',
29
29
  },
30
+ 'bundle-with-expo': {
31
+ description:
32
+ 'Whether to bundle the app with Expo presets or using metro directly',
33
+ default: false,
34
+ demandOption: true,
35
+ type: 'boolean',
36
+ },
30
37
  config: {
31
38
  description: 'Path to the metro config file',
32
39
  type: 'string',
@@ -50,7 +57,8 @@ function main(argv) {
50
57
  type: 'string',
51
58
  },
52
59
  'current-color-dark': {
53
- description: 'Replace any `current` color references in `.dark.svg` SVGs.',
60
+ description:
61
+ 'Replace any `current` color references in `.dark.svg` SVGs.',
54
62
  default: '#ffffff',
55
63
  type: 'string',
56
64
  },
@@ -0,0 +1 @@
1
+ export { Image as default } from 'react-native';