vite-plugin-react-native 0.0.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/README.en.md +103 -0
- package/README.md +103 -0
- package/dist/index.cjs +191 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +189 -0
- package/package.json +48 -0
package/README.en.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# vite-plugin-react-native
|
|
2
|
+
|
|
3
|
+
Vite plugin for using React Native code in web environments, providing React Native to Web compatibility support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ Automatically replaces `react-native` with `react-native-web`
|
|
8
|
+
- ✅ Supports React Native extension resolution (`.web.js`, `.native.js`, etc.)
|
|
9
|
+
- ✅ Provides web-compatible implementations for React Native libraries:
|
|
10
|
+
- `react-native-safe-area-context` - SafeArea components and Hooks
|
|
11
|
+
- `react-native-screens` - Screen components
|
|
12
|
+
- `use-latest-callback` - Callback Hook
|
|
13
|
+
- ✅ Supports both ESM and CommonJS modes
|
|
14
|
+
- ✅ Automatically configures Vite optimization options
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install vite-plugin-react-native
|
|
20
|
+
# or
|
|
21
|
+
yarn add vite-plugin-react-native
|
|
22
|
+
# or
|
|
23
|
+
pnpm add vite-plugin-react-native
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### ESM Mode (Recommended)
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
// vite.config.js or vite.config.ts
|
|
32
|
+
import { defineConfig } from 'vite';
|
|
33
|
+
import { reactNative } from 'vite-plugin-react-native';
|
|
34
|
+
|
|
35
|
+
export default defineConfig({
|
|
36
|
+
plugins: [
|
|
37
|
+
reactNative(),
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### CommonJS Mode
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
// vite.config.js
|
|
46
|
+
const { defineConfig } = require('vite');
|
|
47
|
+
const { reactNative } = require('vite-plugin-react-native');
|
|
48
|
+
|
|
49
|
+
module.exports = defineConfig({
|
|
50
|
+
plugins: [
|
|
51
|
+
reactNative(),
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Compatibility
|
|
57
|
+
|
|
58
|
+
### Supported React Native Libraries
|
|
59
|
+
|
|
60
|
+
- ✅ `react-native` → Automatically replaced with `react-native-web`
|
|
61
|
+
- ✅ `react-native-safe-area-context` → Web-compatible implementation
|
|
62
|
+
- ✅ `react-native-screens` → Web-compatible implementation
|
|
63
|
+
- ✅ `use-latest-callback` → Web-compatible implementation
|
|
64
|
+
|
|
65
|
+
### Vite Version Support
|
|
66
|
+
|
|
67
|
+
- Vite 4.x
|
|
68
|
+
- Vite 5.x
|
|
69
|
+
- Vite 6.x
|
|
70
|
+
- Vite 7.x
|
|
71
|
+
|
|
72
|
+
## How It Works
|
|
73
|
+
|
|
74
|
+
This plugin:
|
|
75
|
+
|
|
76
|
+
1. **Alias Replacement**: Automatically replaces `react-native` with `react-native-web`
|
|
77
|
+
2. **Virtual Modules**: Provides web-compatible virtual implementations for React Native-specific libraries
|
|
78
|
+
3. **Extension Resolution**: Supports React Native file extension conventions (`.web.js`, `.native.js`, etc.)
|
|
79
|
+
4. **Dependency Optimization**: Automatically configures Vite's dependency pre-bundling options
|
|
80
|
+
|
|
81
|
+
## Development
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Install dependencies
|
|
85
|
+
npm install
|
|
86
|
+
|
|
87
|
+
# Build
|
|
88
|
+
npm run build
|
|
89
|
+
|
|
90
|
+
# Will automatically build before publishing
|
|
91
|
+
npm publish
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
ISC
|
|
97
|
+
|
|
98
|
+
## Contributing
|
|
99
|
+
|
|
100
|
+
1. Fork the repository
|
|
101
|
+
2. Create a Feat_xxx branch
|
|
102
|
+
3. Commit your code
|
|
103
|
+
4. Create a Pull Request
|
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# vite-plugin-react-native
|
|
2
|
+
|
|
3
|
+
Vite 插件,用于在 Web 环境中使用 React Native 代码,提供 React Native 到 Web 的兼容性支持。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
- ✅ 自动将 `react-native` 替换为 `react-native-web`
|
|
8
|
+
- ✅ 支持 React Native 扩展名解析(`.web.js`, `.native.js` 等)
|
|
9
|
+
- ✅ 提供 React Native 库的 Web 兼容实现:
|
|
10
|
+
- `react-native-safe-area-context` - SafeArea 组件和 Hooks
|
|
11
|
+
- `react-native-screens` - Screen 组件
|
|
12
|
+
- `use-latest-callback` - 回调 Hook
|
|
13
|
+
- ✅ 支持 ESM 和 CommonJS 双模式
|
|
14
|
+
- ✅ 自动配置 Vite 优化选项
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install vite-plugin-react-native
|
|
20
|
+
# 或
|
|
21
|
+
yarn add vite-plugin-react-native
|
|
22
|
+
# 或
|
|
23
|
+
pnpm add vite-plugin-react-native
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 使用方法
|
|
27
|
+
|
|
28
|
+
### ESM 模式(推荐)
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
// vite.config.js 或 vite.config.ts
|
|
32
|
+
import { defineConfig } from 'vite';
|
|
33
|
+
import { reactNative } from 'vite-plugin-react-native';
|
|
34
|
+
|
|
35
|
+
export default defineConfig({
|
|
36
|
+
plugins: [
|
|
37
|
+
reactNative(),
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### CommonJS 模式
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
// vite.config.js
|
|
46
|
+
const { defineConfig } = require('vite');
|
|
47
|
+
const { reactNative } = require('vite-plugin-react-native');
|
|
48
|
+
|
|
49
|
+
module.exports = defineConfig({
|
|
50
|
+
plugins: [
|
|
51
|
+
reactNative(),
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 支持情况
|
|
57
|
+
|
|
58
|
+
### 兼容的 React Native 库
|
|
59
|
+
|
|
60
|
+
- ✅ `react-native` → 自动替换为 `react-native-web`
|
|
61
|
+
- ✅ `react-native-safe-area-context` → Web 兼容实现
|
|
62
|
+
- ✅ `react-native-screens` → Web 兼容实现
|
|
63
|
+
- ✅ `use-latest-callback` → Web 兼容实现
|
|
64
|
+
|
|
65
|
+
### Vite 版本支持
|
|
66
|
+
|
|
67
|
+
- Vite 4.x
|
|
68
|
+
- Vite 5.x
|
|
69
|
+
- Vite 6.x
|
|
70
|
+
- Vite 7.x
|
|
71
|
+
|
|
72
|
+
## 工作原理
|
|
73
|
+
|
|
74
|
+
该插件会:
|
|
75
|
+
|
|
76
|
+
1. **别名替换**:将 `react-native` 自动替换为 `react-native-web`
|
|
77
|
+
2. **虚拟模块**:为 React Native 特定库提供 Web 兼容的虚拟实现
|
|
78
|
+
3. **扩展名解析**:支持 React Native 的文件扩展名约定(`.web.js`, `.native.js` 等)
|
|
79
|
+
4. **依赖优化**:自动配置 Vite 的依赖预构建选项
|
|
80
|
+
|
|
81
|
+
## 开发
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# 安装依赖
|
|
85
|
+
npm install
|
|
86
|
+
|
|
87
|
+
# 构建
|
|
88
|
+
npm run build
|
|
89
|
+
|
|
90
|
+
# 发布前会自动构建
|
|
91
|
+
npm publish
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 许可证
|
|
95
|
+
|
|
96
|
+
ISC
|
|
97
|
+
|
|
98
|
+
## 参与贡献
|
|
99
|
+
|
|
100
|
+
1. Fork 本仓库
|
|
101
|
+
2. 新建 Feat_xxx 分支
|
|
102
|
+
3. 提交代码
|
|
103
|
+
4. 新建 Pull Request
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// index.ts
|
|
4
|
+
function uniqueStrings(values) {
|
|
5
|
+
const out = [];
|
|
6
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7
|
+
for (const v of values) {
|
|
8
|
+
if (!v) continue;
|
|
9
|
+
if (seen.has(v)) continue;
|
|
10
|
+
seen.add(v);
|
|
11
|
+
out.push(v);
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
var reactNativeResolveExtensions = [
|
|
16
|
+
".web.mjs",
|
|
17
|
+
".web.js",
|
|
18
|
+
".web.ts",
|
|
19
|
+
".web.tsx",
|
|
20
|
+
".web.jsx",
|
|
21
|
+
".native.mjs",
|
|
22
|
+
".native.js",
|
|
23
|
+
".native.ts",
|
|
24
|
+
".native.tsx",
|
|
25
|
+
".native.jsx",
|
|
26
|
+
".mjs",
|
|
27
|
+
".js",
|
|
28
|
+
".ts",
|
|
29
|
+
".tsx",
|
|
30
|
+
".jsx",
|
|
31
|
+
".json"
|
|
32
|
+
];
|
|
33
|
+
var useLatestCallbackShimModule = `
|
|
34
|
+
import * as React from 'react';
|
|
35
|
+
const useClientLayoutEffect =
|
|
36
|
+
typeof document !== 'undefined' ||
|
|
37
|
+
(typeof navigator !== 'undefined' && navigator.product === 'ReactNative')
|
|
38
|
+
? React.useLayoutEffect
|
|
39
|
+
: React.useEffect;
|
|
40
|
+
export default function useLatestCallback(callback) {
|
|
41
|
+
const ref = React.useRef(callback);
|
|
42
|
+
const latestCallback = React.useRef(function latestCallback() {
|
|
43
|
+
return ref.current.apply(this, arguments);
|
|
44
|
+
}).current;
|
|
45
|
+
useClientLayoutEffect(() => {
|
|
46
|
+
ref.current = callback;
|
|
47
|
+
});
|
|
48
|
+
return latestCallback;
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
function latestCallbackEsbuildShim() {
|
|
52
|
+
const namespace = "virtual-use-latest-callback";
|
|
53
|
+
return {
|
|
54
|
+
name: "use-latest-callback-esbuild-shim",
|
|
55
|
+
setup(build) {
|
|
56
|
+
build.onResolve({ filter: /^use-latest-callback$/ }, () => {
|
|
57
|
+
return { path: "use-latest-callback", namespace };
|
|
58
|
+
});
|
|
59
|
+
build.onLoad({ filter: /.*/, namespace }, () => {
|
|
60
|
+
return { contents: useLatestCallbackShimModule, loader: "js" };
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function reactNative() {
|
|
66
|
+
const safeAreaId = "virtual:rn-safe-area-context";
|
|
67
|
+
const resolvedSafeAreaId = "\0" + safeAreaId;
|
|
68
|
+
const screensId = "virtual:rn-screens";
|
|
69
|
+
const resolvedScreensId = "\0" + screensId;
|
|
70
|
+
const latestCallbackId = "virtual:use-latest-callback";
|
|
71
|
+
const resolvedLatestCallbackId = "\0" + latestCallbackId;
|
|
72
|
+
return {
|
|
73
|
+
name: "vite-plugin-react-native",
|
|
74
|
+
enforce: "pre",
|
|
75
|
+
config(userConfig, env) {
|
|
76
|
+
const mode = env.mode;
|
|
77
|
+
const isProd = mode === "production";
|
|
78
|
+
const userResolveExtensions = Array.isArray(userConfig.resolve?.extensions) ? userConfig.resolve?.extensions : null;
|
|
79
|
+
const userOptimizeDepsExclude = Array.isArray(userConfig.optimizeDeps?.exclude) ? userConfig.optimizeDeps?.exclude : [];
|
|
80
|
+
const userOptimizeDepsInclude = Array.isArray(userConfig.optimizeDeps?.include) ? userConfig.optimizeDeps?.include : [];
|
|
81
|
+
const userEsbuildPlugins = Array.isArray(userConfig.optimizeDeps?.esbuildOptions?.plugins) ? userConfig.optimizeDeps?.esbuildOptions?.plugins : [];
|
|
82
|
+
const config = {
|
|
83
|
+
resolve: {
|
|
84
|
+
alias: [{ find: /^react-native$/, replacement: "react-native-web" }],
|
|
85
|
+
dedupe: ["react", "react-dom"],
|
|
86
|
+
extensions: userResolveExtensions ? void 0 : reactNativeResolveExtensions
|
|
87
|
+
},
|
|
88
|
+
define: {
|
|
89
|
+
__DEV__: JSON.stringify(!isProd),
|
|
90
|
+
"process.env.NODE_ENV": JSON.stringify(mode),
|
|
91
|
+
global: "globalThis"
|
|
92
|
+
},
|
|
93
|
+
optimizeDeps: {
|
|
94
|
+
include: uniqueStrings([
|
|
95
|
+
...userOptimizeDepsInclude,
|
|
96
|
+
"react",
|
|
97
|
+
"react-dom",
|
|
98
|
+
"react/jsx-runtime",
|
|
99
|
+
"react/jsx-dev-runtime"
|
|
100
|
+
]),
|
|
101
|
+
exclude: uniqueStrings([
|
|
102
|
+
...userOptimizeDepsExclude,
|
|
103
|
+
"react-native",
|
|
104
|
+
"react-native-gesture-handler",
|
|
105
|
+
"react-native-safe-area-context",
|
|
106
|
+
"react-native-screens"
|
|
107
|
+
]),
|
|
108
|
+
esbuildOptions: {
|
|
109
|
+
plugins: [...userEsbuildPlugins, latestCallbackEsbuildShim()]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
return config;
|
|
114
|
+
},
|
|
115
|
+
resolveId(source) {
|
|
116
|
+
if (source === "react-native-safe-area-context") return resolvedSafeAreaId;
|
|
117
|
+
if (source === "react-native-screens") return resolvedScreensId;
|
|
118
|
+
if (source === "use-latest-callback") return resolvedLatestCallbackId;
|
|
119
|
+
if (source === "use-latest-callback/esm.mjs") return resolvedLatestCallbackId;
|
|
120
|
+
if (source.endsWith("use-latest-callback/lib/src/index.js")) return resolvedLatestCallbackId;
|
|
121
|
+
if (source === "./lib/src/index.js") return resolvedLatestCallbackId;
|
|
122
|
+
return null;
|
|
123
|
+
},
|
|
124
|
+
load(id) {
|
|
125
|
+
const normalizedId = id.split("?")[0];
|
|
126
|
+
if (id === resolvedSafeAreaId) {
|
|
127
|
+
return `
|
|
128
|
+
import * as React from 'react';
|
|
129
|
+
import { View } from 'react-native';
|
|
130
|
+
export const SafeAreaInsetsContext = React.createContext(null);
|
|
131
|
+
export const SafeAreaFrameContext = React.createContext(null);
|
|
132
|
+
const zeroInsets = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
133
|
+
const initialFrame = {
|
|
134
|
+
x: 0,
|
|
135
|
+
y: 0,
|
|
136
|
+
width: typeof window !== 'undefined' ? window.innerWidth : 0,
|
|
137
|
+
height: typeof window !== 'undefined' ? window.innerHeight : 0,
|
|
138
|
+
};
|
|
139
|
+
export const initialWindowMetrics = { frame: initialFrame, insets: zeroInsets };
|
|
140
|
+
export function SafeAreaProvider(props) {
|
|
141
|
+
const { children, initialMetrics, initialSafeAreaInsets, ...rest } = props ?? {};
|
|
142
|
+
const insets = (initialMetrics && initialMetrics.insets) || initialSafeAreaInsets || zeroInsets;
|
|
143
|
+
const frame = (initialMetrics && initialMetrics.frame) || initialFrame;
|
|
144
|
+
return React.createElement(
|
|
145
|
+
SafeAreaFrameContext.Provider,
|
|
146
|
+
{ value: frame },
|
|
147
|
+
React.createElement(
|
|
148
|
+
SafeAreaInsetsContext.Provider,
|
|
149
|
+
{ value: insets },
|
|
150
|
+
React.createElement(View, rest, children),
|
|
151
|
+
),
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
export function useSafeAreaInsets() {
|
|
155
|
+
return React.useContext(SafeAreaInsetsContext) || zeroInsets;
|
|
156
|
+
}
|
|
157
|
+
export function useSafeAreaFrame() {
|
|
158
|
+
return React.useContext(SafeAreaFrameContext) || initialFrame;
|
|
159
|
+
}
|
|
160
|
+
export function SafeAreaView(props) {
|
|
161
|
+
return React.createElement(View, props);
|
|
162
|
+
}
|
|
163
|
+
`;
|
|
164
|
+
}
|
|
165
|
+
if (id === resolvedScreensId) {
|
|
166
|
+
return `
|
|
167
|
+
import * as React from 'react';
|
|
168
|
+
import { View } from 'react-native';
|
|
169
|
+
export function enableScreens() {}
|
|
170
|
+
export function screensEnabled() { return false; }
|
|
171
|
+
export function enableFreeze() {}
|
|
172
|
+
export function freezeEnabled() { return false; }
|
|
173
|
+
export function Screen(props) { return React.createElement(View, props); }
|
|
174
|
+
export function ScreenContainer(props) { return React.createElement(View, props); }
|
|
175
|
+
`;
|
|
176
|
+
}
|
|
177
|
+
if (id === resolvedLatestCallbackId) {
|
|
178
|
+
return useLatestCallbackShimModule;
|
|
179
|
+
}
|
|
180
|
+
if (normalizedId.includes("/node_modules/use-latest-callback/lib/src/index.js")) {
|
|
181
|
+
return useLatestCallbackShimModule;
|
|
182
|
+
}
|
|
183
|
+
if (normalizedId.includes("/node_modules/use-latest-callback/esm.mjs")) {
|
|
184
|
+
return useLatestCallbackShimModule;
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
exports.reactNative = reactNative;
|
package/dist/index.d.cts
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// index.ts
|
|
2
|
+
function uniqueStrings(values) {
|
|
3
|
+
const out = [];
|
|
4
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5
|
+
for (const v of values) {
|
|
6
|
+
if (!v) continue;
|
|
7
|
+
if (seen.has(v)) continue;
|
|
8
|
+
seen.add(v);
|
|
9
|
+
out.push(v);
|
|
10
|
+
}
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
var reactNativeResolveExtensions = [
|
|
14
|
+
".web.mjs",
|
|
15
|
+
".web.js",
|
|
16
|
+
".web.ts",
|
|
17
|
+
".web.tsx",
|
|
18
|
+
".web.jsx",
|
|
19
|
+
".native.mjs",
|
|
20
|
+
".native.js",
|
|
21
|
+
".native.ts",
|
|
22
|
+
".native.tsx",
|
|
23
|
+
".native.jsx",
|
|
24
|
+
".mjs",
|
|
25
|
+
".js",
|
|
26
|
+
".ts",
|
|
27
|
+
".tsx",
|
|
28
|
+
".jsx",
|
|
29
|
+
".json"
|
|
30
|
+
];
|
|
31
|
+
var useLatestCallbackShimModule = `
|
|
32
|
+
import * as React from 'react';
|
|
33
|
+
const useClientLayoutEffect =
|
|
34
|
+
typeof document !== 'undefined' ||
|
|
35
|
+
(typeof navigator !== 'undefined' && navigator.product === 'ReactNative')
|
|
36
|
+
? React.useLayoutEffect
|
|
37
|
+
: React.useEffect;
|
|
38
|
+
export default function useLatestCallback(callback) {
|
|
39
|
+
const ref = React.useRef(callback);
|
|
40
|
+
const latestCallback = React.useRef(function latestCallback() {
|
|
41
|
+
return ref.current.apply(this, arguments);
|
|
42
|
+
}).current;
|
|
43
|
+
useClientLayoutEffect(() => {
|
|
44
|
+
ref.current = callback;
|
|
45
|
+
});
|
|
46
|
+
return latestCallback;
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
function latestCallbackEsbuildShim() {
|
|
50
|
+
const namespace = "virtual-use-latest-callback";
|
|
51
|
+
return {
|
|
52
|
+
name: "use-latest-callback-esbuild-shim",
|
|
53
|
+
setup(build) {
|
|
54
|
+
build.onResolve({ filter: /^use-latest-callback$/ }, () => {
|
|
55
|
+
return { path: "use-latest-callback", namespace };
|
|
56
|
+
});
|
|
57
|
+
build.onLoad({ filter: /.*/, namespace }, () => {
|
|
58
|
+
return { contents: useLatestCallbackShimModule, loader: "js" };
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function reactNative() {
|
|
64
|
+
const safeAreaId = "virtual:rn-safe-area-context";
|
|
65
|
+
const resolvedSafeAreaId = "\0" + safeAreaId;
|
|
66
|
+
const screensId = "virtual:rn-screens";
|
|
67
|
+
const resolvedScreensId = "\0" + screensId;
|
|
68
|
+
const latestCallbackId = "virtual:use-latest-callback";
|
|
69
|
+
const resolvedLatestCallbackId = "\0" + latestCallbackId;
|
|
70
|
+
return {
|
|
71
|
+
name: "vite-plugin-react-native",
|
|
72
|
+
enforce: "pre",
|
|
73
|
+
config(userConfig, env) {
|
|
74
|
+
const mode = env.mode;
|
|
75
|
+
const isProd = mode === "production";
|
|
76
|
+
const userResolveExtensions = Array.isArray(userConfig.resolve?.extensions) ? userConfig.resolve?.extensions : null;
|
|
77
|
+
const userOptimizeDepsExclude = Array.isArray(userConfig.optimizeDeps?.exclude) ? userConfig.optimizeDeps?.exclude : [];
|
|
78
|
+
const userOptimizeDepsInclude = Array.isArray(userConfig.optimizeDeps?.include) ? userConfig.optimizeDeps?.include : [];
|
|
79
|
+
const userEsbuildPlugins = Array.isArray(userConfig.optimizeDeps?.esbuildOptions?.plugins) ? userConfig.optimizeDeps?.esbuildOptions?.plugins : [];
|
|
80
|
+
const config = {
|
|
81
|
+
resolve: {
|
|
82
|
+
alias: [{ find: /^react-native$/, replacement: "react-native-web" }],
|
|
83
|
+
dedupe: ["react", "react-dom"],
|
|
84
|
+
extensions: userResolveExtensions ? void 0 : reactNativeResolveExtensions
|
|
85
|
+
},
|
|
86
|
+
define: {
|
|
87
|
+
__DEV__: JSON.stringify(!isProd),
|
|
88
|
+
"process.env.NODE_ENV": JSON.stringify(mode),
|
|
89
|
+
global: "globalThis"
|
|
90
|
+
},
|
|
91
|
+
optimizeDeps: {
|
|
92
|
+
include: uniqueStrings([
|
|
93
|
+
...userOptimizeDepsInclude,
|
|
94
|
+
"react",
|
|
95
|
+
"react-dom",
|
|
96
|
+
"react/jsx-runtime",
|
|
97
|
+
"react/jsx-dev-runtime"
|
|
98
|
+
]),
|
|
99
|
+
exclude: uniqueStrings([
|
|
100
|
+
...userOptimizeDepsExclude,
|
|
101
|
+
"react-native",
|
|
102
|
+
"react-native-gesture-handler",
|
|
103
|
+
"react-native-safe-area-context",
|
|
104
|
+
"react-native-screens"
|
|
105
|
+
]),
|
|
106
|
+
esbuildOptions: {
|
|
107
|
+
plugins: [...userEsbuildPlugins, latestCallbackEsbuildShim()]
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
return config;
|
|
112
|
+
},
|
|
113
|
+
resolveId(source) {
|
|
114
|
+
if (source === "react-native-safe-area-context") return resolvedSafeAreaId;
|
|
115
|
+
if (source === "react-native-screens") return resolvedScreensId;
|
|
116
|
+
if (source === "use-latest-callback") return resolvedLatestCallbackId;
|
|
117
|
+
if (source === "use-latest-callback/esm.mjs") return resolvedLatestCallbackId;
|
|
118
|
+
if (source.endsWith("use-latest-callback/lib/src/index.js")) return resolvedLatestCallbackId;
|
|
119
|
+
if (source === "./lib/src/index.js") return resolvedLatestCallbackId;
|
|
120
|
+
return null;
|
|
121
|
+
},
|
|
122
|
+
load(id) {
|
|
123
|
+
const normalizedId = id.split("?")[0];
|
|
124
|
+
if (id === resolvedSafeAreaId) {
|
|
125
|
+
return `
|
|
126
|
+
import * as React from 'react';
|
|
127
|
+
import { View } from 'react-native';
|
|
128
|
+
export const SafeAreaInsetsContext = React.createContext(null);
|
|
129
|
+
export const SafeAreaFrameContext = React.createContext(null);
|
|
130
|
+
const zeroInsets = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
131
|
+
const initialFrame = {
|
|
132
|
+
x: 0,
|
|
133
|
+
y: 0,
|
|
134
|
+
width: typeof window !== 'undefined' ? window.innerWidth : 0,
|
|
135
|
+
height: typeof window !== 'undefined' ? window.innerHeight : 0,
|
|
136
|
+
};
|
|
137
|
+
export const initialWindowMetrics = { frame: initialFrame, insets: zeroInsets };
|
|
138
|
+
export function SafeAreaProvider(props) {
|
|
139
|
+
const { children, initialMetrics, initialSafeAreaInsets, ...rest } = props ?? {};
|
|
140
|
+
const insets = (initialMetrics && initialMetrics.insets) || initialSafeAreaInsets || zeroInsets;
|
|
141
|
+
const frame = (initialMetrics && initialMetrics.frame) || initialFrame;
|
|
142
|
+
return React.createElement(
|
|
143
|
+
SafeAreaFrameContext.Provider,
|
|
144
|
+
{ value: frame },
|
|
145
|
+
React.createElement(
|
|
146
|
+
SafeAreaInsetsContext.Provider,
|
|
147
|
+
{ value: insets },
|
|
148
|
+
React.createElement(View, rest, children),
|
|
149
|
+
),
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
export function useSafeAreaInsets() {
|
|
153
|
+
return React.useContext(SafeAreaInsetsContext) || zeroInsets;
|
|
154
|
+
}
|
|
155
|
+
export function useSafeAreaFrame() {
|
|
156
|
+
return React.useContext(SafeAreaFrameContext) || initialFrame;
|
|
157
|
+
}
|
|
158
|
+
export function SafeAreaView(props) {
|
|
159
|
+
return React.createElement(View, props);
|
|
160
|
+
}
|
|
161
|
+
`;
|
|
162
|
+
}
|
|
163
|
+
if (id === resolvedScreensId) {
|
|
164
|
+
return `
|
|
165
|
+
import * as React from 'react';
|
|
166
|
+
import { View } from 'react-native';
|
|
167
|
+
export function enableScreens() {}
|
|
168
|
+
export function screensEnabled() { return false; }
|
|
169
|
+
export function enableFreeze() {}
|
|
170
|
+
export function freezeEnabled() { return false; }
|
|
171
|
+
export function Screen(props) { return React.createElement(View, props); }
|
|
172
|
+
export function ScreenContainer(props) { return React.createElement(View, props); }
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
if (id === resolvedLatestCallbackId) {
|
|
176
|
+
return useLatestCallbackShimModule;
|
|
177
|
+
}
|
|
178
|
+
if (normalizedId.includes("/node_modules/use-latest-callback/lib/src/index.js")) {
|
|
179
|
+
return useLatestCallbackShimModule;
|
|
180
|
+
}
|
|
181
|
+
if (normalizedId.includes("/node_modules/use-latest-callback/esm.mjs")) {
|
|
182
|
+
return useLatestCallbackShimModule;
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export { reactNative };
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vite-plugin-react-native",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Vite plugin for React Native web compatibility",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"vite",
|
|
7
|
+
"vite-plugin",
|
|
8
|
+
"react-native",
|
|
9
|
+
"react-native-web",
|
|
10
|
+
"web",
|
|
11
|
+
"compatibility"
|
|
12
|
+
],
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://gitee.com/ws18250840411/vite-plugin-react-native.git"
|
|
16
|
+
},
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"author": "",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"main": "./dist/index.cjs",
|
|
21
|
+
"module": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js",
|
|
27
|
+
"require": "./dist/index.cjs"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsup",
|
|
36
|
+
"prepublishOnly": "npm run build",
|
|
37
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^20.0.0",
|
|
44
|
+
"tsup": "^8.5.1",
|
|
45
|
+
"typescript": "^5.0.0",
|
|
46
|
+
"vite": "^7.0.0"
|
|
47
|
+
}
|
|
48
|
+
}
|