vite-plugin-react-native 0.0.3 → 1.0.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 +128 -57
- package/dist/index.cjs +426 -89
- package/dist/index.d.cts +80 -2
- package/dist/index.d.ts +80 -2
- package/dist/index.js +410 -89
- package/package.json +6 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
|
-
import { Plugin } from 'vite';
|
|
1
|
+
import { AliasOptions, Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
declare function flowRemoveTypesPlugin(): {
|
|
4
|
+
name: string;
|
|
5
|
+
transform: {
|
|
6
|
+
filter: {
|
|
7
|
+
code: RegExp;
|
|
8
|
+
};
|
|
9
|
+
handler: (code: string) => {
|
|
10
|
+
code: string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
declare function stripFlowTypes(code: string): string;
|
|
15
|
+
declare function treeshakeFixPlugin(): {
|
|
16
|
+
name: string;
|
|
17
|
+
transform: {
|
|
18
|
+
handler: (_code: string, id: string) => {
|
|
19
|
+
moduleSideEffects: string;
|
|
20
|
+
} | null;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
declare function transformCssInteropDoctorCheck(code: string, id: string): {
|
|
24
|
+
code: string;
|
|
25
|
+
map: null;
|
|
26
|
+
changed: boolean;
|
|
27
|
+
};
|
|
28
|
+
declare function transformReanimatedWebUtils(code: string, id: string, isProduction: boolean): {
|
|
29
|
+
code: string;
|
|
30
|
+
map: null;
|
|
31
|
+
changed: boolean;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** Vite 配置:别名、define、build、optimizeDeps */
|
|
35
|
+
|
|
36
|
+
type AliasEntry = {
|
|
37
|
+
find: string | RegExp;
|
|
38
|
+
replacement: string;
|
|
39
|
+
};
|
|
40
|
+
declare function normalizeUserAlias(alias: AliasOptions): AliasEntry[];
|
|
41
|
+
declare function buildResolveAlias(options: {
|
|
42
|
+
userAliasArray: AliasEntry[];
|
|
43
|
+
routerDomEntry: string | null;
|
|
44
|
+
}): AliasEntry[];
|
|
45
|
+
|
|
46
|
+
/** 常量与工具:扩展名、optimizeDeps 列表、去重、Vite 版本、动态 include */
|
|
47
|
+
declare const RESOLVE_EXTENSIONS: readonly [".web.mjs", ".web.js", ".web.ts", ".web.tsx", ".web.jsx", ".mjs", ".js", ".ts", ".tsx", ".jsx", ".json"];
|
|
48
|
+
declare const MODULE_TYPES: {
|
|
49
|
+
readonly '.js': "jsx";
|
|
50
|
+
readonly '.mjs': "jsx";
|
|
51
|
+
readonly '.cjs': "jsx";
|
|
52
|
+
readonly '.flow': "jsx";
|
|
53
|
+
};
|
|
54
|
+
declare const TREESHAKE_SAFE_PRESET: {
|
|
55
|
+
annotations: boolean;
|
|
56
|
+
invalidImportSideEffects: boolean;
|
|
57
|
+
manualPureFunctions: string[];
|
|
58
|
+
moduleSideEffects: boolean;
|
|
59
|
+
propertyReadSideEffects: "always";
|
|
60
|
+
unknownGlobalSideEffects: boolean;
|
|
61
|
+
propertyWriteSideEffects: "always";
|
|
62
|
+
};
|
|
63
|
+
declare const CORE_OPTIMIZE_DEPS_INCLUDE: readonly ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime", "react-native-web"];
|
|
64
|
+
declare const OPTIONAL_OPTIMIZE_DEPS_INCLUDE: readonly ["hoist-non-react-statics", "invariant", "react-native-reanimated", "react-native-css-interop", "react-native-i18njs", "@react-native/normalize-colors", "inline-style-prefixer", "inline-style-prefixer/lib/plugins/crossFade", "css-in-js-utils"];
|
|
65
|
+
type RequireLike = {
|
|
66
|
+
resolve: (id: string, options?: {
|
|
67
|
+
paths?: string[];
|
|
68
|
+
}) => string;
|
|
69
|
+
};
|
|
70
|
+
declare function uniqueStrings(values: Array<string | undefined>): string[];
|
|
71
|
+
/** 从项目解析的 Vite 主版本号,用于判断是否使用 rolldownOptions(Vite 8+) */
|
|
72
|
+
declare function getViteMajorVersion(projectRoot: string, require: RequireLike): number;
|
|
73
|
+
/**
|
|
74
|
+
* 仅包含项目已安装的依赖,避免 optimizeDeps.include 里未安装的包导致解析失败。
|
|
75
|
+
* 核心列表 + 可选列表中在 package.json 里出现的项。
|
|
76
|
+
*/
|
|
77
|
+
declare function getInstalledOptimizeDepsInclude(projectRoot: string, routerDomEntry: string | null): string[];
|
|
78
|
+
|
|
79
|
+
/** Vite 插件:React Native Web 兼容(别名、虚拟模块、Flow、optimizeDeps) */
|
|
2
80
|
|
|
3
81
|
declare function reactNative(): Plugin;
|
|
4
82
|
|
|
5
|
-
export { reactNative };
|
|
83
|
+
export { CORE_OPTIMIZE_DEPS_INCLUDE, MODULE_TYPES, OPTIONAL_OPTIMIZE_DEPS_INCLUDE, RESOLVE_EXTENSIONS, TREESHAKE_SAFE_PRESET, buildResolveAlias, flowRemoveTypesPlugin, getInstalledOptimizeDepsInclude, getViteMajorVersion, normalizeUserAlias, reactNative, stripFlowTypes, transformCssInteropDoctorCheck, transformReanimatedWebUtils, treeshakeFixPlugin, uniqueStrings };
|
package/dist/index.js
CHANGED
|
@@ -1,21 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path2 from 'path';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import { createRequire } from 'module';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
|
+
import { transformWithEsbuild } from 'vite';
|
|
6
|
+
import flowRemoveTypes from 'flow-remove-types';
|
|
5
7
|
|
|
6
|
-
// index.ts
|
|
7
|
-
|
|
8
|
-
const out = [];
|
|
9
|
-
const seen = /* @__PURE__ */ new Set();
|
|
10
|
-
for (const v of values) {
|
|
11
|
-
if (!v) continue;
|
|
12
|
-
if (seen.has(v)) continue;
|
|
13
|
-
seen.add(v);
|
|
14
|
-
out.push(v);
|
|
15
|
-
}
|
|
16
|
-
return out;
|
|
17
|
-
}
|
|
18
|
-
var webResolveExtensions = [
|
|
8
|
+
// src/index.ts
|
|
9
|
+
var RESOLVE_EXTENSIONS = [
|
|
19
10
|
".web.mjs",
|
|
20
11
|
".web.js",
|
|
21
12
|
".web.ts",
|
|
@@ -28,81 +19,290 @@ var webResolveExtensions = [
|
|
|
28
19
|
".jsx",
|
|
29
20
|
".json"
|
|
30
21
|
];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
22
|
+
var MODULE_TYPES = { ".js": "jsx", ".mjs": "jsx", ".cjs": "jsx", ".flow": "jsx" };
|
|
23
|
+
var TREESHAKE_SAFE_PRESET = {
|
|
24
|
+
annotations: true,
|
|
25
|
+
invalidImportSideEffects: true,
|
|
26
|
+
manualPureFunctions: [],
|
|
27
|
+
moduleSideEffects: true,
|
|
28
|
+
propertyReadSideEffects: "always",
|
|
29
|
+
unknownGlobalSideEffects: true,
|
|
30
|
+
propertyWriteSideEffects: "always"
|
|
31
|
+
};
|
|
32
|
+
var CORE_OPTIMIZE_DEPS_INCLUDE = [
|
|
33
|
+
"react",
|
|
34
|
+
"react-dom",
|
|
35
|
+
"react/jsx-runtime",
|
|
36
|
+
"react/jsx-dev-runtime",
|
|
37
|
+
"react-native-web"
|
|
38
|
+
];
|
|
39
|
+
var OPTIONAL_OPTIMIZE_DEPS_INCLUDE = [
|
|
40
|
+
"hoist-non-react-statics",
|
|
41
|
+
"invariant",
|
|
42
|
+
"react-native-reanimated",
|
|
43
|
+
"react-native-css-interop",
|
|
44
|
+
"react-native-i18njs",
|
|
45
|
+
"@react-native/normalize-colors",
|
|
46
|
+
"inline-style-prefixer",
|
|
47
|
+
"inline-style-prefixer/lib/plugins/crossFade",
|
|
48
|
+
"css-in-js-utils"
|
|
49
|
+
];
|
|
50
|
+
function uniqueStrings(values) {
|
|
51
|
+
const seen = /* @__PURE__ */ new Set();
|
|
52
|
+
const out = [];
|
|
53
|
+
for (const v of values) {
|
|
54
|
+
if (!v || seen.has(v)) continue;
|
|
55
|
+
seen.add(v);
|
|
56
|
+
out.push(v);
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
function getViteMajorVersion(projectRoot, require2) {
|
|
61
|
+
try {
|
|
62
|
+
const pkgPath = require2.resolve("vite/package.json", {
|
|
63
|
+
paths: [projectRoot]
|
|
64
|
+
});
|
|
65
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
66
|
+
const v = String(pkg.version ?? "0");
|
|
67
|
+
const major = Number(v.split(".")[0]);
|
|
68
|
+
return Number.isFinite(major) ? major : 0;
|
|
69
|
+
} catch {
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function getInstalledOptimizeDepsInclude(projectRoot, routerDomEntry) {
|
|
74
|
+
const pkgPath = path2.join(projectRoot, "package.json");
|
|
75
|
+
let deps = {};
|
|
76
|
+
try {
|
|
77
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
78
|
+
deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
79
|
+
} catch {
|
|
80
|
+
return [...CORE_OPTIMIZE_DEPS_INCLUDE];
|
|
81
|
+
}
|
|
82
|
+
const installed = OPTIONAL_OPTIMIZE_DEPS_INCLUDE.filter((entry) => {
|
|
83
|
+
const pkgName = entry.split("/")[0];
|
|
84
|
+
return pkgName in deps;
|
|
85
|
+
});
|
|
86
|
+
const list = [...CORE_OPTIMIZE_DEPS_INCLUDE, ...installed];
|
|
87
|
+
if (routerDomEntry && "react-native-router-dom" in deps) {
|
|
88
|
+
list.push("react-native-router-dom");
|
|
89
|
+
}
|
|
90
|
+
return list;
|
|
91
|
+
}
|
|
92
|
+
var DOCTOR_PATTERN = /return\s*<react-native-css-interop-jsx-pragma-check\s*\/>\s*===\s*true\s*;/g;
|
|
93
|
+
function flowRemoveTypesPlugin() {
|
|
38
94
|
return {
|
|
39
|
-
name: "
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
dedupe: ["react", "react-dom"],
|
|
63
|
-
extensions: userResolveExtensions ? void 0 : webResolveExtensions
|
|
64
|
-
},
|
|
65
|
-
build: {
|
|
66
|
-
commonjsOptions: {
|
|
67
|
-
esmExternals: true,
|
|
68
|
-
requireReturnsDefault: "auto",
|
|
69
|
-
transformMixedEsModules: true
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
define: {
|
|
73
|
-
__DEV__: JSON.stringify(!isProd),
|
|
74
|
-
"process.env.NODE_ENV": JSON.stringify(mode),
|
|
75
|
-
global: "globalThis"
|
|
76
|
-
},
|
|
77
|
-
optimizeDeps: {
|
|
78
|
-
include: uniqueStrings([
|
|
79
|
-
...userOptimizeDepsInclude,
|
|
80
|
-
"react",
|
|
81
|
-
"react-dom",
|
|
82
|
-
"react/jsx-runtime",
|
|
83
|
-
"react/jsx-dev-runtime",
|
|
84
|
-
"hoist-non-react-statics",
|
|
85
|
-
"invariant"
|
|
86
|
-
]),
|
|
87
|
-
exclude: uniqueStrings([
|
|
88
|
-
...userOptimizeDepsExclude,
|
|
89
|
-
"react-native",
|
|
90
|
-
"react-native-gesture-handler",
|
|
91
|
-
"react-native-safe-area-context",
|
|
92
|
-
"react-native-screens"
|
|
93
|
-
])
|
|
95
|
+
name: "flow-remove-types",
|
|
96
|
+
transform: {
|
|
97
|
+
filter: { code: /@flow/ },
|
|
98
|
+
handler(code) {
|
|
99
|
+
return { code: flowRemoveTypes(code).toString() };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function stripFlowTypes(code) {
|
|
105
|
+
try {
|
|
106
|
+
return flowRemoveTypes(code).toString();
|
|
107
|
+
} catch {
|
|
108
|
+
return code;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function treeshakeFixPlugin() {
|
|
112
|
+
return {
|
|
113
|
+
name: "treeshake-fix",
|
|
114
|
+
transform: {
|
|
115
|
+
handler(_code, id) {
|
|
116
|
+
if (id.includes("react-native-css-interop") || id.includes("react-native-css") || id.includes("expo-modules-core")) {
|
|
117
|
+
return { moduleSideEffects: "no-treeshake" };
|
|
94
118
|
}
|
|
95
|
-
|
|
96
|
-
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function transformCssInteropDoctorCheck(code, id) {
|
|
125
|
+
if (!id.includes("node_modules/react-native-css-interop") || !id.includes("dist/doctor.js")) {
|
|
126
|
+
return { code, map: null, changed: false };
|
|
127
|
+
}
|
|
128
|
+
const re = new RegExp(DOCTOR_PATTERN.source, "g");
|
|
129
|
+
if (!re.test(code)) return { code, map: null, changed: false };
|
|
130
|
+
re.lastIndex = 0;
|
|
131
|
+
return { code: code.replace(re, "return true;"), map: null, changed: true };
|
|
132
|
+
}
|
|
133
|
+
function analyzeReanimatedRequires(code, exportedVars) {
|
|
134
|
+
const importMap = /* @__PURE__ */ new Map();
|
|
135
|
+
for (const varName of exportedVars) {
|
|
136
|
+
const re = new RegExp(
|
|
137
|
+
`${varName}\\s*=\\s*[\\s\\S]*?require\\(['"]([^'"]+)['"]\\)(?:\\.([\\w]+))?`,
|
|
138
|
+
"g"
|
|
139
|
+
);
|
|
140
|
+
const m = re.exec(code);
|
|
141
|
+
if (m) {
|
|
142
|
+
const [, modulePath, prop] = m;
|
|
143
|
+
const key = `${modulePath}:${prop || "default"}`;
|
|
144
|
+
if (!importMap.has(key)) importMap.set(key, []);
|
|
145
|
+
importMap.get(key).push(varName);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return importMap;
|
|
149
|
+
}
|
|
150
|
+
function generateReanimatedExports(importMap) {
|
|
151
|
+
let out = "";
|
|
152
|
+
for (const [key, vars] of importMap) {
|
|
153
|
+
const [modulePath, prop] = key.split(":");
|
|
154
|
+
for (const v of vars) {
|
|
155
|
+
out += prop === "default" ? `export { default as ${v} } from '${modulePath}';
|
|
156
|
+
` : `export { ${prop} as ${v} } from '${modulePath}';
|
|
157
|
+
`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return out;
|
|
161
|
+
}
|
|
162
|
+
var REANIMATED_TRY_CATCH_RE = /try\s*\{[^{}]*?require\([^)]+\)[^{}]*?\}\s*catch[^{}]*?\{[^{}]*?\}/gs;
|
|
163
|
+
function transformReanimatedWebUtils(code, id, isProduction) {
|
|
164
|
+
if (!isProduction || !id.includes("node_modules/react-native-reanimated") || !id.includes("ReanimatedModule/js-reanimated/webUtils") || !code.includes("export let") || !code.includes("try") || !code.includes("require")) {
|
|
165
|
+
return { code, map: null, changed: false };
|
|
166
|
+
}
|
|
167
|
+
const exportLetMatches = [...code.matchAll(/export let (\w+);/g)];
|
|
168
|
+
const exportedVars = exportLetMatches.map((m) => m[1]);
|
|
169
|
+
if (!exportedVars.length) return { code, map: null, changed: false };
|
|
170
|
+
const importMap = analyzeReanimatedRequires(code, exportedVars);
|
|
171
|
+
let result = code.replace(REANIMATED_TRY_CATCH_RE, "\n").replace(/export let \w+;/g, "");
|
|
172
|
+
const exports$1 = generateReanimatedExports(importMap);
|
|
173
|
+
if (!exports$1) return { code, map: null, changed: false };
|
|
174
|
+
result = result.trimEnd() + "\n\n" + exports$1;
|
|
175
|
+
return { code: result, map: null, changed: true };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/config.ts
|
|
179
|
+
function normalizeUserAlias(alias) {
|
|
180
|
+
if (Array.isArray(alias)) return alias;
|
|
181
|
+
if (alias && typeof alias === "object") {
|
|
182
|
+
return Object.entries(alias).map(([find, replacement]) => ({ find, replacement: String(replacement) }));
|
|
183
|
+
}
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
function buildResolveAlias(options) {
|
|
187
|
+
const { userAliasArray, routerDomEntry } = options;
|
|
188
|
+
const alias = [...userAliasArray];
|
|
189
|
+
if (routerDomEntry) {
|
|
190
|
+
alias.push({ find: /^react-native-router-dom$/, replacement: routerDomEntry });
|
|
191
|
+
}
|
|
192
|
+
alias.push(
|
|
193
|
+
{ find: /^react-native\/(?!Libraries\/Core\/Devtools\/openURLInBrowser$)(.*)$/, replacement: "react-native-web/$1" },
|
|
194
|
+
{ find: "inline-style-prefixer/lib/createPrefixer", replacement: "inline-style-prefixer/es/createPrefixer" },
|
|
195
|
+
{ find: /^inline-style-prefixer\/lib\/plugins\/(.*)$/, replacement: "inline-style-prefixer/es/plugins/$1" },
|
|
196
|
+
{ find: /^css-in-js-utils\/lib\/(.*)$/, replacement: "css-in-js-utils/es/$1" },
|
|
197
|
+
{ find: "react-native/Libraries/EventEmitter/RCTDeviceEventEmitter", replacement: "react-native-web/dist/vendor/react-native/NativeEventEmitter/RCTDeviceEventEmitter" },
|
|
198
|
+
{ find: "react-native/Libraries/vendor/emitter/EventEmitter", replacement: "react-native-web/dist/vendor/react-native/emitter/EventEmitter" },
|
|
199
|
+
{ find: "react-native/Libraries/EventEmitter/NativeEventEmitter", replacement: "react-native-web/dist/vendor/react-native/NativeEventEmitter" }
|
|
200
|
+
);
|
|
201
|
+
return alias;
|
|
202
|
+
}
|
|
203
|
+
function buildViteConfig(ctx) {
|
|
204
|
+
const { mode, userConfig, resolveExtensions, useRolldown, installedInclude, routerDomEntry } = ctx;
|
|
205
|
+
const isProd = mode === "production";
|
|
206
|
+
const userAliasArray = normalizeUserAlias(userConfig.resolve?.alias ?? []);
|
|
207
|
+
const alias = buildResolveAlias({ userAliasArray, routerDomEntry });
|
|
208
|
+
const od = userConfig.optimizeDeps;
|
|
209
|
+
const userOptimizeDepsExclude = Array.isArray(od?.exclude) ? od.exclude : [];
|
|
210
|
+
const userOptimizeDepsInclude = Array.isArray(od?.include) ? od.include : [];
|
|
211
|
+
const userEsbuildOptions = od?.esbuildOptions && typeof od.esbuildOptions === "object" ? od.esbuildOptions : {};
|
|
212
|
+
const userRolldownOptions = od?.rolldownOptions && typeof od.rolldownOptions === "object" ? od.rolldownOptions : void 0;
|
|
213
|
+
const rolldownPlugins = [flowRemoveTypesPlugin(), treeshakeFixPlugin()];
|
|
214
|
+
return {
|
|
215
|
+
resolve: {
|
|
216
|
+
alias,
|
|
217
|
+
dedupe: ["react", "react-dom"],
|
|
218
|
+
extensions: Array.isArray(userConfig.resolve?.extensions) ? void 0 : [...resolveExtensions]
|
|
97
219
|
},
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
220
|
+
build: useRolldown ? {
|
|
221
|
+
rolldownOptions: {
|
|
222
|
+
resolve: { extensions: [...resolveExtensions] },
|
|
223
|
+
shimMissingExports: true,
|
|
224
|
+
treeshake: TREESHAKE_SAFE_PRESET,
|
|
225
|
+
moduleTypes: MODULE_TYPES,
|
|
226
|
+
plugins: rolldownPlugins
|
|
227
|
+
}
|
|
228
|
+
} : {
|
|
229
|
+
commonjsOptions: {
|
|
230
|
+
esmExternals: true,
|
|
231
|
+
requireReturnsDefault: "auto",
|
|
232
|
+
transformMixedEsModules: true
|
|
233
|
+
}
|
|
102
234
|
},
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
235
|
+
define: {
|
|
236
|
+
__DEV__: JSON.stringify(!isProd),
|
|
237
|
+
"process.env.NODE_ENV": JSON.stringify(mode),
|
|
238
|
+
"process.env.EXPO_OS": JSON.stringify("web"),
|
|
239
|
+
global: "globalThis",
|
|
240
|
+
_WORKLET: false,
|
|
241
|
+
EXPO_OS: JSON.stringify("web")
|
|
242
|
+
},
|
|
243
|
+
optimizeDeps: {
|
|
244
|
+
...useRolldown ? {
|
|
245
|
+
rolldownOptions: {
|
|
246
|
+
...userRolldownOptions,
|
|
247
|
+
resolve: {
|
|
248
|
+
...userRolldownOptions?.resolve,
|
|
249
|
+
extensions: [...resolveExtensions]
|
|
250
|
+
},
|
|
251
|
+
shimMissingExports: true,
|
|
252
|
+
treeshake: TREESHAKE_SAFE_PRESET,
|
|
253
|
+
moduleTypes: MODULE_TYPES,
|
|
254
|
+
plugins: [
|
|
255
|
+
...rolldownPlugins,
|
|
256
|
+
...userRolldownOptions?.plugins ?? []
|
|
257
|
+
]
|
|
258
|
+
}
|
|
259
|
+
} : {
|
|
260
|
+
esbuildOptions: {
|
|
261
|
+
...userEsbuildOptions,
|
|
262
|
+
resolveExtensions: "resolveExtensions" in userEsbuildOptions ? userEsbuildOptions.resolveExtensions : resolveExtensions,
|
|
263
|
+
loader: {
|
|
264
|
+
..."loader" in userEsbuildOptions ? userEsbuildOptions.loader : {},
|
|
265
|
+
".js": "jsx"
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
include: uniqueStrings([...userOptimizeDepsInclude, ...installedInclude]),
|
|
270
|
+
exclude: uniqueStrings([
|
|
271
|
+
...userOptimizeDepsExclude,
|
|
272
|
+
"react-native",
|
|
273
|
+
"react-native-gesture-handler",
|
|
274
|
+
"react-native-safe-area-context",
|
|
275
|
+
"react-native-screens",
|
|
276
|
+
"@react-native/new-app-screen"
|
|
277
|
+
])
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/virtuals.ts
|
|
283
|
+
var VIRTUAL_RN_WEB = "virtual:rn-web";
|
|
284
|
+
var VIRTUAL_OPEN_URL_IN_BROWSER = "virtual:rn-openURLInBrowser";
|
|
285
|
+
var VIRTUAL_SAFE_AREA = "virtual:rn-safe-area-context";
|
|
286
|
+
var VIRTUAL_SCREENS = "virtual:rn-screens";
|
|
287
|
+
var VIRTUAL_GESTURE_HANDLER = "virtual:rn-gesture-handler";
|
|
288
|
+
var RESOLVED_RN_WEB = "\0" + VIRTUAL_RN_WEB;
|
|
289
|
+
var RESOLVED_OPEN_URL_IN_BROWSER = "\0" + VIRTUAL_OPEN_URL_IN_BROWSER;
|
|
290
|
+
var RESOLVED_SAFE_AREA = "\0" + VIRTUAL_SAFE_AREA;
|
|
291
|
+
var RESOLVED_SCREENS = "\0" + VIRTUAL_SCREENS;
|
|
292
|
+
var RESOLVED_GESTURE_HANDLER = "\0" + VIRTUAL_GESTURE_HANDLER;
|
|
293
|
+
var RN_LIBRARIES_OPEN_URL = "react-native/Libraries/Core/Devtools/openURLInBrowser";
|
|
294
|
+
function getVirtualModuleContent(id) {
|
|
295
|
+
if (id === RESOLVED_RN_WEB) {
|
|
296
|
+
return [
|
|
297
|
+
"export * from 'react-native-web';",
|
|
298
|
+
"export const ReactNativeVersion = { major: 0, minor: 84, patch: 1, prerelease: null, getVersionString() { return this.major + '.' + this.minor + '.' + this.patch + (this.prerelease ? '-' + this.prerelease : ''); } };"
|
|
299
|
+
].join("\n");
|
|
300
|
+
}
|
|
301
|
+
if (id === RESOLVED_OPEN_URL_IN_BROWSER) {
|
|
302
|
+
return "export default function openURLInBrowser(url) { if (typeof window !== 'undefined' && url) window.open(url, '_blank'); }\n";
|
|
303
|
+
}
|
|
304
|
+
if (id === RESOLVED_SAFE_AREA) {
|
|
305
|
+
return `
|
|
106
306
|
import * as React from 'react';
|
|
107
307
|
import { View } from 'react-native';
|
|
108
308
|
export const SafeAreaInsetsContext = React.createContext(null);
|
|
@@ -139,9 +339,9 @@ export function SafeAreaView(props) {
|
|
|
139
339
|
return React.createElement(View, props);
|
|
140
340
|
}
|
|
141
341
|
`;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
342
|
+
}
|
|
343
|
+
if (id === RESOLVED_SCREENS) {
|
|
344
|
+
return `
|
|
145
345
|
import * as React from 'react';
|
|
146
346
|
import { View } from 'react-native';
|
|
147
347
|
export function enableScreens() {}
|
|
@@ -164,10 +364,131 @@ export function ScreenContainer(props) {
|
|
|
164
364
|
return React.createElement(View, { ...rest, style: withFlex1Style(style) });
|
|
165
365
|
}
|
|
166
366
|
`;
|
|
367
|
+
}
|
|
368
|
+
if (id === RESOLVED_GESTURE_HANDLER) {
|
|
369
|
+
return `
|
|
370
|
+
import * as React from 'react';
|
|
371
|
+
import { View } from 'react-native';
|
|
372
|
+
|
|
373
|
+
export function GestureHandlerRootView(props) {
|
|
374
|
+
return React.createElement(View, props);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export const Gesture = {
|
|
378
|
+
Tap: () => null,
|
|
379
|
+
Pan: () => null,
|
|
380
|
+
Pinch: () => null,
|
|
381
|
+
LongPress: () => null,
|
|
382
|
+
Fling: () => null,
|
|
383
|
+
Native: () => null,
|
|
384
|
+
Manual: () => null,
|
|
385
|
+
Race: () => null,
|
|
386
|
+
Simultaneous: () => null,
|
|
387
|
+
Exclusive: () => null,
|
|
388
|
+
};
|
|
389
|
+
export function GestureDetector(props) {
|
|
390
|
+
return React.createElement(View, props);
|
|
391
|
+
}
|
|
392
|
+
export default { GestureHandlerRootView, Gesture, GestureDetector };
|
|
393
|
+
`;
|
|
394
|
+
}
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
function resolveVirtualId(source) {
|
|
398
|
+
if (source === "react-native") return RESOLVED_RN_WEB;
|
|
399
|
+
if (source === RN_LIBRARIES_OPEN_URL) return RESOLVED_OPEN_URL_IN_BROWSER;
|
|
400
|
+
if (source === "react-native-safe-area-context") return RESOLVED_SAFE_AREA;
|
|
401
|
+
if (source === "react-native-screens") return RESOLVED_SCREENS;
|
|
402
|
+
if (source === "react-native-gesture-handler") return RESOLVED_GESTURE_HANDLER;
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// src/index.ts
|
|
407
|
+
function replaceRequireAssetsWithImports(code) {
|
|
408
|
+
const re = /require\s*\(\s*['"](\.\/assets\/[^'"]+)['"]\s*\)/g;
|
|
409
|
+
const matches = [...code.matchAll(re)];
|
|
410
|
+
if (!matches.length) return code;
|
|
411
|
+
const seen = /* @__PURE__ */ new Map();
|
|
412
|
+
const imports = [];
|
|
413
|
+
let i = 0;
|
|
414
|
+
for (const m of matches) {
|
|
415
|
+
const p = m[1];
|
|
416
|
+
if (seen.has(p)) continue;
|
|
417
|
+
const name = `__rn_asset_${i++}`;
|
|
418
|
+
seen.set(p, name);
|
|
419
|
+
imports.push(`import ${name} from '${p}';`);
|
|
420
|
+
}
|
|
421
|
+
let out = code.replace(re, (_, p) => seen.get(p) ?? _);
|
|
422
|
+
const at = Math.max(0, out.search(/\n?import\s+/));
|
|
423
|
+
return out.slice(0, at) + imports.join("\n") + "\n" + out.slice(at);
|
|
424
|
+
}
|
|
425
|
+
function reactNative() {
|
|
426
|
+
const pluginDir = path2.dirname(fileURLToPath(import.meta.url));
|
|
427
|
+
const require2 = createRequire(import.meta.url);
|
|
428
|
+
let buildMode = "development";
|
|
429
|
+
return {
|
|
430
|
+
name: "react-native",
|
|
431
|
+
enforce: "pre",
|
|
432
|
+
config(userConfig, env) {
|
|
433
|
+
buildMode = env.mode ?? "development";
|
|
434
|
+
const projectRoot = userConfig.root ? path2.resolve(userConfig.root) : process.cwd();
|
|
435
|
+
let routerDomEntry = null;
|
|
436
|
+
try {
|
|
437
|
+
const routerDomPkg = require2.resolve("react-native-router-dom/package.json", { paths: [projectRoot, pluginDir] });
|
|
438
|
+
const routerDomDir = path2.dirname(routerDomPkg);
|
|
439
|
+
routerDomEntry = fs.existsSync(path2.join(routerDomDir, "src/index.ts")) ? path2.join(routerDomDir, "src/index.ts") : path2.join(routerDomDir, "dist/index.mjs");
|
|
440
|
+
} catch {
|
|
441
|
+
}
|
|
442
|
+
const useRolldown = getViteMajorVersion(projectRoot, require2) >= 8;
|
|
443
|
+
const resolveExtensions = userConfig.resolve?.extensions ?? [...RESOLVE_EXTENSIONS];
|
|
444
|
+
const installedInclude = getInstalledOptimizeDepsInclude(projectRoot, routerDomEntry);
|
|
445
|
+
const partial = buildViteConfig({
|
|
446
|
+
mode: buildMode,
|
|
447
|
+
userConfig,
|
|
448
|
+
resolveExtensions,
|
|
449
|
+
useRolldown,
|
|
450
|
+
installedInclude,
|
|
451
|
+
routerDomEntry
|
|
452
|
+
});
|
|
453
|
+
return partial;
|
|
454
|
+
},
|
|
455
|
+
resolveId(source) {
|
|
456
|
+
const resolved = resolveVirtualId(source);
|
|
457
|
+
return resolved ?? null;
|
|
458
|
+
},
|
|
459
|
+
load(id) {
|
|
460
|
+
const content = getVirtualModuleContent(id);
|
|
461
|
+
return content ?? null;
|
|
462
|
+
},
|
|
463
|
+
async transform(code, id) {
|
|
464
|
+
const idPath = id.split("?")[0];
|
|
465
|
+
const isNodeModulesJs = id.includes("node_modules") && /\.(js|jsx|mjs|cjs)$/.test(idPath);
|
|
466
|
+
const hasFlow = /@flow/.test(code);
|
|
467
|
+
const isReactNativePkg = /node_modules[/\\]@react-native[/\\]/.test(id) || /node_modules[/\\]react-native[/\\]/.test(id);
|
|
468
|
+
let resultCode = code;
|
|
469
|
+
if (isNodeModulesJs && (hasFlow || isReactNativePkg)) {
|
|
470
|
+
resultCode = stripFlowTypes(resultCode);
|
|
471
|
+
}
|
|
472
|
+
if (id.includes("node_modules/@react-native") && /require\s*\(\s*['"]\.\/assets\//.test(resultCode)) {
|
|
473
|
+
resultCode = replaceRequireAssetsWithImports(resultCode);
|
|
474
|
+
}
|
|
475
|
+
const cssInterop = transformCssInteropDoctorCheck(resultCode, id);
|
|
476
|
+
if (cssInterop.changed) resultCode = cssInterop.code;
|
|
477
|
+
const reanimated = transformReanimatedWebUtils(resultCode, id, buildMode === "production");
|
|
478
|
+
if (reanimated.changed) resultCode = reanimated.code;
|
|
479
|
+
if (id.includes("node_modules/@react-native") && /\.js$/.test(idPath) && /<[A-Za-z]/.test(resultCode)) {
|
|
480
|
+
const result = await transformWithEsbuild(resultCode, idPath, {
|
|
481
|
+
loader: "jsx",
|
|
482
|
+
jsx: "automatic"
|
|
483
|
+
});
|
|
484
|
+
return { code: result.code, map: result.map ?? null };
|
|
485
|
+
}
|
|
486
|
+
if (resultCode !== code || cssInterop.changed || reanimated.changed) {
|
|
487
|
+
return { code: resultCode, map: null };
|
|
167
488
|
}
|
|
168
489
|
return null;
|
|
169
490
|
}
|
|
170
491
|
};
|
|
171
492
|
}
|
|
172
493
|
|
|
173
|
-
export { reactNative };
|
|
494
|
+
export { CORE_OPTIMIZE_DEPS_INCLUDE, MODULE_TYPES, OPTIONAL_OPTIMIZE_DEPS_INCLUDE, RESOLVE_EXTENSIONS, TREESHAKE_SAFE_PRESET, buildResolveAlias, flowRemoveTypesPlugin, getInstalledOptimizeDepsInclude, getViteMajorVersion, normalizeUserAlias, reactNative, stripFlowTypes, transformCssInteropDoctorCheck, transformReanimatedWebUtils, treeshakeFixPlugin, uniqueStrings };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-react-native",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Vite plugin for React Native web compatibility",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vite",
|
|
@@ -37,7 +37,10 @@
|
|
|
37
37
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
40
|
+
"vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"flow-remove-types": "^2.305.1"
|
|
41
44
|
},
|
|
42
45
|
"devDependencies": {
|
|
43
46
|
"@types/node": "^20.0.0",
|
|
@@ -45,4 +48,4 @@
|
|
|
45
48
|
"typescript": "^5.0.0",
|
|
46
49
|
"vite": "^7.0.0"
|
|
47
50
|
}
|
|
48
|
-
}
|
|
51
|
+
}
|