miaoda-expo-devkit 0.1.1-beta.52 → 0.1.1-beta.54
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/biome-config.json +3 -0
- package/dist/metro.d.mts +10 -4
- package/dist/metro.d.ts +10 -4
- package/dist/metro.js +9 -8
- package/dist/metro.mjs +9 -8
- package/dist/stubs/expo-file-system-next-stub.js +203 -0
- package/dist/stubs/expo-image-picker-stub.js +39 -3
- package/oxlint-config.json +2 -1
- package/package.json +1 -1
package/biome-config.json
CHANGED
package/dist/metro.d.mts
CHANGED
|
@@ -298,14 +298,20 @@ declare function withExpoMediaLibraryStub(config: MetroConfig): MetroConfig;
|
|
|
298
298
|
declare function withExpoCalendarStub(config: MetroConfig): MetroConfig;
|
|
299
299
|
|
|
300
300
|
/**
|
|
301
|
-
* withExpoFileSystemStub — Web 端将 expo-file-system
|
|
301
|
+
* withExpoFileSystemStub — Web 端将 expo-file-system 系列模块替换为 stub
|
|
302
|
+
*
|
|
303
|
+
* expo-file-system 的文件 API 在 Web 端不可用:
|
|
304
|
+
* - legacy API:底层 shim 方法均缺失(会抛 UnavailabilityError)
|
|
305
|
+
* - 新版 API(File / Directory / Paths):ExpoFileSystem.web.ts 基类缺少 validatePath(),
|
|
306
|
+
* new File(...) / new Directory(...) 会抛 TypeError: this.validatePath is not a function
|
|
302
307
|
*
|
|
303
|
-
* expo-file-system 的文件 API 在 Web 端不可用(底层 shim 方法均缺失)。
|
|
304
308
|
* stub 在运行时通过 Platform.OS 判断:
|
|
305
309
|
* - Web:no-op(核心 API 弹 Dialog 提示,不崩溃)
|
|
306
|
-
* - Expo Go / Development Build
|
|
310
|
+
* - Expo Go / Development Build(原生):透传真实模块,功能完全正常
|
|
307
311
|
*
|
|
308
|
-
*
|
|
312
|
+
* 拦截路径:
|
|
313
|
+
* - expo-file-system/legacy → expo-file-system-stub.js (legacy API)
|
|
314
|
+
* - expo-file-system → expo-file-system-next-stub.js (新版 File/Directory/Paths API)
|
|
309
315
|
*/
|
|
310
316
|
|
|
311
317
|
/**
|
package/dist/metro.d.ts
CHANGED
|
@@ -298,14 +298,20 @@ declare function withExpoMediaLibraryStub(config: MetroConfig): MetroConfig;
|
|
|
298
298
|
declare function withExpoCalendarStub(config: MetroConfig): MetroConfig;
|
|
299
299
|
|
|
300
300
|
/**
|
|
301
|
-
* withExpoFileSystemStub — Web 端将 expo-file-system
|
|
301
|
+
* withExpoFileSystemStub — Web 端将 expo-file-system 系列模块替换为 stub
|
|
302
|
+
*
|
|
303
|
+
* expo-file-system 的文件 API 在 Web 端不可用:
|
|
304
|
+
* - legacy API:底层 shim 方法均缺失(会抛 UnavailabilityError)
|
|
305
|
+
* - 新版 API(File / Directory / Paths):ExpoFileSystem.web.ts 基类缺少 validatePath(),
|
|
306
|
+
* new File(...) / new Directory(...) 会抛 TypeError: this.validatePath is not a function
|
|
302
307
|
*
|
|
303
|
-
* expo-file-system 的文件 API 在 Web 端不可用(底层 shim 方法均缺失)。
|
|
304
308
|
* stub 在运行时通过 Platform.OS 判断:
|
|
305
309
|
* - Web:no-op(核心 API 弹 Dialog 提示,不崩溃)
|
|
306
|
-
* - Expo Go / Development Build
|
|
310
|
+
* - Expo Go / Development Build(原生):透传真实模块,功能完全正常
|
|
307
311
|
*
|
|
308
|
-
*
|
|
312
|
+
* 拦截路径:
|
|
313
|
+
* - expo-file-system/legacy → expo-file-system-stub.js (legacy API)
|
|
314
|
+
* - expo-file-system → expo-file-system-next-stub.js (新版 File/Directory/Paths API)
|
|
309
315
|
*/
|
|
310
316
|
|
|
311
317
|
/**
|
package/dist/metro.js
CHANGED
|
@@ -410,17 +410,18 @@ function withExpoCalendarStub(config) {
|
|
|
410
410
|
|
|
411
411
|
// src/metro/withExpoFileSystemStub.ts
|
|
412
412
|
var import_path11 = __toESM(require("path"));
|
|
413
|
-
var
|
|
414
|
-
var
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
EXPO_FILE_SYSTEM_STUB_FILENAME
|
|
418
|
-
);
|
|
413
|
+
var LEGACY_STUB_FILENAME = "expo-file-system-stub.js";
|
|
414
|
+
var LEGACY_STUB_PATH = import_path11.default.resolve(__dirname, "stubs", LEGACY_STUB_FILENAME);
|
|
415
|
+
var NEXT_STUB_FILENAME = "expo-file-system-next-stub.js";
|
|
416
|
+
var NEXT_STUB_PATH = import_path11.default.resolve(__dirname, "stubs", NEXT_STUB_FILENAME);
|
|
419
417
|
function withExpoFileSystemStub(config) {
|
|
420
418
|
const upstream = config.resolver?.resolveRequest ?? null;
|
|
421
419
|
const resolveRequest = (context, moduleName, platform) => {
|
|
422
|
-
if (moduleName === "expo-file-system/legacy" && !context.originModulePath.includes(
|
|
423
|
-
return { filePath:
|
|
420
|
+
if (moduleName === "expo-file-system/legacy" && !context.originModulePath.includes(LEGACY_STUB_FILENAME)) {
|
|
421
|
+
return { filePath: LEGACY_STUB_PATH, type: "sourceFile" };
|
|
422
|
+
}
|
|
423
|
+
if (moduleName === "expo-file-system" && !context.originModulePath.includes(NEXT_STUB_FILENAME)) {
|
|
424
|
+
return { filePath: NEXT_STUB_PATH, type: "sourceFile" };
|
|
424
425
|
}
|
|
425
426
|
if (upstream) return upstream(context, moduleName, platform);
|
|
426
427
|
return context.resolveRequest(context, moduleName, platform);
|
package/dist/metro.mjs
CHANGED
|
@@ -365,17 +365,18 @@ function withExpoCalendarStub(config) {
|
|
|
365
365
|
|
|
366
366
|
// src/metro/withExpoFileSystemStub.ts
|
|
367
367
|
import path11 from "path";
|
|
368
|
-
var
|
|
369
|
-
var
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
EXPO_FILE_SYSTEM_STUB_FILENAME
|
|
373
|
-
);
|
|
368
|
+
var LEGACY_STUB_FILENAME = "expo-file-system-stub.js";
|
|
369
|
+
var LEGACY_STUB_PATH = path11.resolve(__dirname, "stubs", LEGACY_STUB_FILENAME);
|
|
370
|
+
var NEXT_STUB_FILENAME = "expo-file-system-next-stub.js";
|
|
371
|
+
var NEXT_STUB_PATH = path11.resolve(__dirname, "stubs", NEXT_STUB_FILENAME);
|
|
374
372
|
function withExpoFileSystemStub(config) {
|
|
375
373
|
const upstream = config.resolver?.resolveRequest ?? null;
|
|
376
374
|
const resolveRequest = (context, moduleName, platform) => {
|
|
377
|
-
if (moduleName === "expo-file-system/legacy" && !context.originModulePath.includes(
|
|
378
|
-
return { filePath:
|
|
375
|
+
if (moduleName === "expo-file-system/legacy" && !context.originModulePath.includes(LEGACY_STUB_FILENAME)) {
|
|
376
|
+
return { filePath: LEGACY_STUB_PATH, type: "sourceFile" };
|
|
377
|
+
}
|
|
378
|
+
if (moduleName === "expo-file-system" && !context.originModulePath.includes(NEXT_STUB_FILENAME)) {
|
|
379
|
+
return { filePath: NEXT_STUB_PATH, type: "sourceFile" };
|
|
379
380
|
}
|
|
380
381
|
if (upstream) return upstream(context, moduleName, platform);
|
|
381
382
|
return context.resolveRequest(context, moduleName, platform);
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var import_react_native = require("react-native");
|
|
3
|
+
var import_web_stub_dialog = require("./web-stub-dialog");
|
|
4
|
+
if (import_react_native.Platform.OS !== "web") {
|
|
5
|
+
module.exports = require("expo-file-system");
|
|
6
|
+
} else {
|
|
7
|
+
let joinUris = function(...parts) {
|
|
8
|
+
const strings = parts.map((p) => typeof p === "string" ? p : p.uri);
|
|
9
|
+
if (strings.length === 0) return "";
|
|
10
|
+
let result = strings[0] ?? "";
|
|
11
|
+
for (let i = 1; i < strings.length; i++) {
|
|
12
|
+
const part = strings[i] ?? "";
|
|
13
|
+
result = result.replace(/\/?$/, "/") + part.replace(/^\//, "");
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
}, uriBasename = function(uri) {
|
|
17
|
+
return uri.split("/").filter(Boolean).pop() ?? "";
|
|
18
|
+
}, uriDirname = function(uri) {
|
|
19
|
+
const parts = uri.split("/");
|
|
20
|
+
parts.pop();
|
|
21
|
+
return parts.join("/") || "/";
|
|
22
|
+
};
|
|
23
|
+
var joinUris2 = joinUris, uriBasename2 = uriBasename, uriDirname2 = uriDirname;
|
|
24
|
+
console.warn("[devkit] expo-file-system-next-stub loaded (web)");
|
|
25
|
+
class Directory {
|
|
26
|
+
constructor(...uris) {
|
|
27
|
+
this.uri = joinUris(...uris);
|
|
28
|
+
}
|
|
29
|
+
/** 真实基类方法,此处 no-op 防止 TypeError */
|
|
30
|
+
validatePath() {
|
|
31
|
+
}
|
|
32
|
+
get exists() {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
create(_options) {
|
|
36
|
+
(0, import_web_stub_dialog.showWebStubDialog)({ title: "\u521B\u5EFA\u76EE\u5F55", details: [`URI: ${this.uri}`] });
|
|
37
|
+
}
|
|
38
|
+
delete() {
|
|
39
|
+
(0, import_web_stub_dialog.showWebStubDialog)({ title: "\u5220\u9664\u76EE\u5F55", details: [`URI: ${this.uri}`] });
|
|
40
|
+
}
|
|
41
|
+
list() {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
listAsRecords() {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
get name() {
|
|
48
|
+
return uriBasename(this.uri);
|
|
49
|
+
}
|
|
50
|
+
get parentDirectory() {
|
|
51
|
+
return new Directory(uriDirname(this.uri));
|
|
52
|
+
}
|
|
53
|
+
createFile(name, _mimeType) {
|
|
54
|
+
return new File(this, name);
|
|
55
|
+
}
|
|
56
|
+
createDirectory(name) {
|
|
57
|
+
return new Directory(this, name);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
class File {
|
|
61
|
+
constructor(...uris) {
|
|
62
|
+
this.uri = joinUris(...uris);
|
|
63
|
+
}
|
|
64
|
+
/** 真实基类方法,此处 no-op 防止 TypeError */
|
|
65
|
+
validatePath() {
|
|
66
|
+
}
|
|
67
|
+
get exists() {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
get size() {
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
get type() {
|
|
74
|
+
return "";
|
|
75
|
+
}
|
|
76
|
+
get extension() {
|
|
77
|
+
const name = uriBasename(this.uri);
|
|
78
|
+
const dot = name.lastIndexOf(".");
|
|
79
|
+
return dot >= 0 ? name.slice(dot) : "";
|
|
80
|
+
}
|
|
81
|
+
get name() {
|
|
82
|
+
return uriBasename(this.uri);
|
|
83
|
+
}
|
|
84
|
+
get parentDirectory() {
|
|
85
|
+
return new Directory(uriDirname(this.uri));
|
|
86
|
+
}
|
|
87
|
+
copy(destination) {
|
|
88
|
+
(0, import_web_stub_dialog.showWebStubDialog)({
|
|
89
|
+
title: "\u590D\u5236\u6587\u4EF6",
|
|
90
|
+
details: [`\u4ECE: ${this.uri}`, `\u5230: ${destination.uri}`]
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
move(destination) {
|
|
94
|
+
(0, import_web_stub_dialog.showWebStubDialog)({
|
|
95
|
+
title: "\u79FB\u52A8\u6587\u4EF6",
|
|
96
|
+
details: [`\u4ECE: ${this.uri}`, `\u5230: ${destination.uri}`]
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
delete() {
|
|
100
|
+
(0, import_web_stub_dialog.showWebStubDialog)({ title: "\u5220\u9664\u6587\u4EF6", details: [`URI: ${this.uri}`] });
|
|
101
|
+
}
|
|
102
|
+
open() {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
// ─── Blob interface(Web 环境所需,返回空值)
|
|
106
|
+
async arrayBuffer() {
|
|
107
|
+
return new ArrayBuffer(0);
|
|
108
|
+
}
|
|
109
|
+
async text() {
|
|
110
|
+
return "";
|
|
111
|
+
}
|
|
112
|
+
async bytes() {
|
|
113
|
+
return new Uint8Array(0);
|
|
114
|
+
}
|
|
115
|
+
bytesSync() {
|
|
116
|
+
return new Uint8Array(0);
|
|
117
|
+
}
|
|
118
|
+
slice(_start, _end, _contentType) {
|
|
119
|
+
return new Blob([]);
|
|
120
|
+
}
|
|
121
|
+
stream() {
|
|
122
|
+
return new ReadableStream();
|
|
123
|
+
}
|
|
124
|
+
readableStream() {
|
|
125
|
+
return new ReadableStream();
|
|
126
|
+
}
|
|
127
|
+
writableStream() {
|
|
128
|
+
return new WritableStream();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
File.downloadFileAsync = async (_url, destination) => {
|
|
132
|
+
(0, import_web_stub_dialog.showWebStubDialog)({ title: "\u4E0B\u8F7D\u6587\u4EF6", details: [`\u76EE\u6807: ${destination.uri}`] });
|
|
133
|
+
return new File(destination.uri);
|
|
134
|
+
};
|
|
135
|
+
File.pickFileAsync = async () => {
|
|
136
|
+
(0, import_web_stub_dialog.showWebStubDialog)({ title: "\u9009\u62E9\u6587\u4EF6", details: [] });
|
|
137
|
+
return new File("document:/stub-pick");
|
|
138
|
+
};
|
|
139
|
+
Directory.pickDirectoryAsync = async () => {
|
|
140
|
+
(0, import_web_stub_dialog.showWebStubDialog)({ title: "\u9009\u62E9\u76EE\u5F55", details: [] });
|
|
141
|
+
return new Directory("document:/stub-pick");
|
|
142
|
+
};
|
|
143
|
+
const Paths = {
|
|
144
|
+
get document() {
|
|
145
|
+
return new Directory("document:/");
|
|
146
|
+
},
|
|
147
|
+
get cache() {
|
|
148
|
+
return new Directory("cache:/");
|
|
149
|
+
},
|
|
150
|
+
get bundle() {
|
|
151
|
+
return new Directory("bundle:/");
|
|
152
|
+
},
|
|
153
|
+
get totalDiskSpace() {
|
|
154
|
+
return 0;
|
|
155
|
+
},
|
|
156
|
+
get availableDiskSpace() {
|
|
157
|
+
return 0;
|
|
158
|
+
},
|
|
159
|
+
get appleSharedContainers() {
|
|
160
|
+
return {};
|
|
161
|
+
},
|
|
162
|
+
join(...uris) {
|
|
163
|
+
return joinUris(...uris);
|
|
164
|
+
},
|
|
165
|
+
basename(uri) {
|
|
166
|
+
return uriBasename(uri);
|
|
167
|
+
},
|
|
168
|
+
dirname(uri) {
|
|
169
|
+
return uriDirname(uri);
|
|
170
|
+
},
|
|
171
|
+
extname(uri) {
|
|
172
|
+
const name = uriBasename(uri);
|
|
173
|
+
const dot = name.lastIndexOf(".");
|
|
174
|
+
return dot >= 0 ? name.slice(dot) : "";
|
|
175
|
+
},
|
|
176
|
+
normalize(uri) {
|
|
177
|
+
return uri;
|
|
178
|
+
},
|
|
179
|
+
isAbsolute(_uri) {
|
|
180
|
+
return true;
|
|
181
|
+
},
|
|
182
|
+
resolve(...uris) {
|
|
183
|
+
return joinUris(...uris);
|
|
184
|
+
},
|
|
185
|
+
relative(_from, to) {
|
|
186
|
+
return to;
|
|
187
|
+
},
|
|
188
|
+
info(_uri) {
|
|
189
|
+
return { isDirectory: false };
|
|
190
|
+
},
|
|
191
|
+
sep: "/",
|
|
192
|
+
delimiter: ":"
|
|
193
|
+
};
|
|
194
|
+
const handlers = { File, Directory, Paths };
|
|
195
|
+
const noop = async () => void 0;
|
|
196
|
+
module.exports = new Proxy(handlers, {
|
|
197
|
+
get(target, key) {
|
|
198
|
+
if (key in target) return target[key];
|
|
199
|
+
return noop;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=expo-file-system-next-stub.js.map
|
|
@@ -105,6 +105,18 @@ function openCameraDialog(facingMode) {
|
|
|
105
105
|
"font-weight:600",
|
|
106
106
|
"font-family:sans-serif"
|
|
107
107
|
].join(";");
|
|
108
|
+
const btnLocal = document.createElement("button");
|
|
109
|
+
btnLocal.textContent = "\u{1F4C1} \u672C\u5730\u9009\u62E9";
|
|
110
|
+
btnLocal.style.cssText = [
|
|
111
|
+
"padding:10px 20px",
|
|
112
|
+
"border-radius:8px",
|
|
113
|
+
"border:1px solid #7C3AED",
|
|
114
|
+
"cursor:pointer",
|
|
115
|
+
"background:transparent",
|
|
116
|
+
"color:#a78bfa",
|
|
117
|
+
"font-size:15px",
|
|
118
|
+
"font-family:sans-serif"
|
|
119
|
+
].join(";");
|
|
108
120
|
const btnCancel = document.createElement("button");
|
|
109
121
|
btnCancel.textContent = "\u53D6\u6D88";
|
|
110
122
|
btnCancel.style.cssText = [
|
|
@@ -120,6 +132,7 @@ function openCameraDialog(facingMode) {
|
|
|
120
132
|
const errorMsg = document.createElement("p");
|
|
121
133
|
errorMsg.style.cssText = "margin:0;color:#f87171;font-size:13px;font-family:sans-serif;display:none";
|
|
122
134
|
btnRow.appendChild(btnCapture);
|
|
135
|
+
btnRow.appendChild(btnLocal);
|
|
123
136
|
btnRow.appendChild(btnCancel);
|
|
124
137
|
body.appendChild(video);
|
|
125
138
|
body.appendChild(btnRow);
|
|
@@ -156,6 +169,32 @@ function openCameraDialog(facingMode) {
|
|
|
156
169
|
cleanup();
|
|
157
170
|
resolve({ canceled: false, dataUrl, mimeType: "image/jpeg" });
|
|
158
171
|
});
|
|
172
|
+
btnLocal.addEventListener("click", () => {
|
|
173
|
+
const fileInput = document.createElement("input");
|
|
174
|
+
fileInput.type = "file";
|
|
175
|
+
fileInput.accept = "image/*";
|
|
176
|
+
fileInput.addEventListener("change", () => {
|
|
177
|
+
const file = fileInput.files?.[0];
|
|
178
|
+
if (!file) return;
|
|
179
|
+
const reader = new FileReader();
|
|
180
|
+
reader.onload = (e) => {
|
|
181
|
+
const dataUrl = e.target?.result;
|
|
182
|
+
if (!dataUrl) {
|
|
183
|
+
cleanup();
|
|
184
|
+
resolve({ canceled: true });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
cleanup();
|
|
188
|
+
resolve({ canceled: false, dataUrl, mimeType: file.type || "image/jpeg" });
|
|
189
|
+
};
|
|
190
|
+
reader.onerror = () => {
|
|
191
|
+
cleanup();
|
|
192
|
+
resolve({ canceled: true });
|
|
193
|
+
};
|
|
194
|
+
reader.readAsDataURL(file);
|
|
195
|
+
});
|
|
196
|
+
fileInput.click();
|
|
197
|
+
});
|
|
159
198
|
btnCancel.addEventListener("click", () => {
|
|
160
199
|
cleanup();
|
|
161
200
|
resolve({ canceled: true });
|
|
@@ -207,9 +246,6 @@ if (import_react_native.Platform.OS === "web") {
|
|
|
207
246
|
showMobilePassthroughToast();
|
|
208
247
|
return ExpoImagePicker.launchCameraAsync(options);
|
|
209
248
|
}
|
|
210
|
-
if (typeof navigator?.mediaDevices?.getUserMedia !== "function") {
|
|
211
|
-
return ExpoImagePicker.launchCameraAsync(options);
|
|
212
|
-
}
|
|
213
249
|
const facingMode = getFacingMode(options);
|
|
214
250
|
const result = await openCameraDialog(facingMode);
|
|
215
251
|
if (result.canceled || !result.dataUrl) {
|
package/oxlint-config.json
CHANGED