miaoda-expo-devkit 0.1.1-beta.98 → 0.1.1-dev.development-build.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.md +154 -110
- package/dist/babel/plugin-lucide-react-native.js +184 -184
- package/dist/cli/lint.js +317 -309
- package/dist/index.d.mts +1 -110
- package/dist/index.d.ts +1 -110
- package/dist/index.js +0 -159
- package/dist/index.mjs +0 -132
- package/dist/metro.d.mts +25 -5
- package/dist/metro.d.ts +25 -5
- package/dist/metro.js +49 -18
- package/dist/metro.mjs +50 -20
- package/dist/rules/no-expo-audio-compat.js +2041 -0
- package/dist/rules/no-expo-video-compat.js +90 -90
- package/dist/stubs/entry-inject.js +0 -2
- package/dist/stubs/expo-native-wechat-stub.js +116 -0
- package/dist/stubs/expo-router-entry-native-stub.js +4 -0
- package/dist/stubs/i18n/en.js +16 -1
- package/dist/stubs/i18n/zh.js +16 -1
- package/dist/stubs/logbox-control.js +9 -0
- package/dist/stubs/screenshot-control.js +1 -1
- package/dist/stubs/sentry-react-native-stub.js +15 -180
- package/oxlint-config.json +11 -4
- package/package.json +60 -5
- package/pnpm-patches/@expo__cli@55.0.16-55.0.22.patch +627 -0
- package/pnpm-patches/@expo__cli@55.0.16.patch +218 -0
- package/pnpm-patches/@expo__cli@55.0.17.patch +218 -0
- package/pnpm-patches/@expo__cli@55.0.18.patch +218 -0
- package/pnpm-patches/@expo__cli@55.0.19.patch +218 -0
- package/pnpm-patches/@expo__cli@55.0.20.patch +218 -0
- package/pnpm-patches/@expo__cli@55.0.21.patch +218 -0
- package/pnpm-patches/@expo__cli@55.0.22.patch +218 -0
- package/pnpm-patches/@shopify__react-native-skia@2.4.18.patch +14 -0
- package/pnpm-patches/expo-modules-core@55.0.15.patch +21 -0
- package/pnpm-patches/expo@55.0.6.patch +235 -0
- package/pnpm-patches/metro-file-map@0.83.3.patch +24 -0
- package/pnpm-patches/metro-runtime@0.83.3.patch +13 -0
- package/pnpm-patches/react-native-css-interop@0.2.6.patch +12 -0
- package/pnpm-patches/react-native-reanimated@4.2.1.patch +53 -0
- package/pnpm-patches/react-native-worklets@0.7.2.patch +91 -0
- package/pnpm-patches/react-native@0.83.2.patch +384 -0
- package/pnpmfile.cjs +168 -0
- package/dist/stubs/preview-control.js +0 -60
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
diff --git a/plugin/index.js b/plugin/index.js
|
|
2
|
+
--- a/plugin/index.js
|
|
3
|
+
+++ b/plugin/index.js
|
|
4
|
+
@@ -752,6 +752,31 @@ var require_workletStringCode = __commonJS({
|
|
5
|
+
var types_2 = require_types();
|
|
6
|
+
var utils_1 = require_utils();
|
|
7
|
+
var MOCK_SOURCE_MAP = "mock source map";
|
|
8
|
+
+ function normalizeTypeScriptFunctionTypes(node) {
|
|
9
|
+
+ if (node == null || typeof node !== "object") {
|
|
10
|
+
+ return;
|
|
11
|
+
+ }
|
|
12
|
+
+ if (node.type === "TSFunctionType" || node.type === "TSConstructorType") {
|
|
13
|
+
+ const parameters = Array.isArray(node.parameters) ? node.parameters : Array.isArray(node.params) ? node.params : [];
|
|
14
|
+
+ node.parameters = parameters;
|
|
15
|
+
+ node.params = parameters;
|
|
16
|
+
+ const returnType = node.typeAnnotation != null ? node.typeAnnotation : node.returnType != null ? node.returnType : null;
|
|
17
|
+
+ node.typeAnnotation = returnType;
|
|
18
|
+
+ node.returnType = returnType;
|
|
19
|
+
+ }
|
|
20
|
+
+ if (node.type === "TSTypeReference") {
|
|
21
|
+
+ const typeArguments = node.typeParameters != null ? node.typeParameters : node.typeArguments != null ? node.typeArguments : null;
|
|
22
|
+
+ node.typeParameters = typeArguments;
|
|
23
|
+
+ node.typeArguments = typeArguments;
|
|
24
|
+
+ }
|
|
25
|
+
+ for (const value of Object.values(node)) {
|
|
26
|
+
+ if (Array.isArray(value)) {
|
|
27
|
+
+ value.forEach(normalizeTypeScriptFunctionTypes);
|
|
28
|
+
+ } else {
|
|
29
|
+
+ normalizeTypeScriptFunctionTypes(value);
|
|
30
|
+
+ }
|
|
31
|
+
+ }
|
|
32
|
+
+ }
|
|
33
|
+
function buildWorkletString(fun, state, closureVariables, workletName, inputMap) {
|
|
34
|
+
var _a;
|
|
35
|
+
restoreRecursiveCalls(fun, workletName);
|
|
36
|
+
@@ -784,6 +809,7 @@ var require_workletStringCode = __commonJS({
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const workletFunction = (0, types_12.functionExpression)((0, types_12.identifier)(workletName), expression.params, expression.body, expression.generator, expression.async);
|
|
40
|
+
+ normalizeTypeScriptFunctionTypes(workletFunction);
|
|
41
|
+
const code = (0, generator_1.default)(workletFunction).code;
|
|
42
|
+
(0, assert_1.strict)(inputMap, "[Reanimated] `inputMap` is undefined.");
|
|
43
|
+
const includeSourceMap = !((0, utils_1.isRelease)() || state.opts.disableSourceMaps);
|
|
44
|
+
@@ -892,10 +918,36 @@ var require_workletFactory = __commonJS({
|
|
45
|
+
var workletStringCode_1 = require_workletStringCode();
|
|
46
|
+
var REAL_VERSION = require("../package.json").version;
|
|
47
|
+
var MOCK_VERSION = "x.y.z";
|
|
48
|
+
+ function normalizeTypeScriptFunctionTypes(node) {
|
|
49
|
+
+ if (node == null || typeof node !== "object") {
|
|
50
|
+
+ return;
|
|
51
|
+
+ }
|
|
52
|
+
+ if (node.type === "TSFunctionType" || node.type === "TSConstructorType") {
|
|
53
|
+
+ const parameters = Array.isArray(node.parameters) ? node.parameters : Array.isArray(node.params) ? node.params : [];
|
|
54
|
+
+ node.parameters = parameters;
|
|
55
|
+
+ node.params = parameters;
|
|
56
|
+
+ const returnType = node.typeAnnotation != null ? node.typeAnnotation : node.returnType != null ? node.returnType : null;
|
|
57
|
+
+ node.typeAnnotation = returnType;
|
|
58
|
+
+ node.returnType = returnType;
|
|
59
|
+
+ }
|
|
60
|
+
+ if (node.type === "TSTypeReference") {
|
|
61
|
+
+ const typeArguments = node.typeParameters != null ? node.typeParameters : node.typeArguments != null ? node.typeArguments : null;
|
|
62
|
+
+ node.typeParameters = typeArguments;
|
|
63
|
+
+ node.typeArguments = typeArguments;
|
|
64
|
+
+ }
|
|
65
|
+
+ for (const value of Object.values(node)) {
|
|
66
|
+
+ if (Array.isArray(value)) {
|
|
67
|
+
+ value.forEach(normalizeTypeScriptFunctionTypes);
|
|
68
|
+
+ } else {
|
|
69
|
+
+ normalizeTypeScriptFunctionTypes(value);
|
|
70
|
+
+ }
|
|
71
|
+
+ }
|
|
72
|
+
+ }
|
|
73
|
+
function makeWorkletFactory(fun, state) {
|
|
74
|
+
var _a;
|
|
75
|
+
removeWorkletDirective(fun);
|
|
76
|
+
(0, assert_1.strict)(state.file.opts.filename, "[Reanimated] `state.file.opts.filename` is undefined.");
|
|
77
|
+
+ normalizeTypeScriptFunctionTypes(fun.node);
|
|
78
|
+
const codeObject = (0, generator_1.default)(fun.node, {
|
|
79
|
+
sourceMaps: true,
|
|
80
|
+
sourceFileName: state.file.opts.filename
|
|
81
|
+
@@ -977,8 +1029,8 @@ var require_workletFactory = __commonJS({
|
|
82
|
+
statements.unshift((0, types_12.variableDeclaration)("const", [
|
|
83
|
+
(0, types_12.variableDeclarator)((0, types_12.identifier)("_e"), (0, types_12.arrayExpression)([
|
|
84
|
+
(0, types_12.newExpression)((0, types_12.memberExpression)((0, types_12.identifier)("global"), (0, types_12.identifier)("Error")), []),
|
|
85
|
+
- (0, types_12.numericLiteral)(lineOffset),
|
|
86
|
+
- (0, types_12.numericLiteral)(-27)
|
|
87
|
+
+ lineOffset < 0 ? (0, types_12.unaryExpression)("-", (0, types_12.numericLiteral)(-lineOffset)) : (0, types_12.numericLiteral)(lineOffset),
|
|
88
|
+
+ (0, types_12.unaryExpression)("-", (0, types_12.numericLiteral)(27))
|
|
89
|
+
]))
|
|
90
|
+
]));
|
|
91
|
+
statements.push((0, types_12.expressionStatement)((0, types_12.assignmentExpression)("=", (0, types_12.memberExpression)((0, types_12.identifier)(reactName), (0, types_12.identifier)("__stackDetails"), false), (0, types_12.identifier)("_e"))));
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
diff --git a/Libraries/LogBox/UI/LogBoxInspector.js b/Libraries/LogBox/UI/LogBoxInspector.js
|
|
2
|
+
--- a/Libraries/LogBox/UI/LogBoxInspector.js
|
|
3
|
+
+++ b/Libraries/LogBox/UI/LogBoxInspector.js
|
|
4
|
+
@@ -8,6 +8,7 @@
|
|
5
|
+
* @format
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
+import Clipboard from '../../Components/Clipboard/Clipboard';
|
|
9
|
+
import Keyboard from '../../Components/Keyboard/Keyboard';
|
|
10
|
+
import View from '../../Components/View/View';
|
|
11
|
+
import StyleSheet from '../../StyleSheet/StyleSheet';
|
|
12
|
+
@@ -18,7 +19,97 @@
|
|
13
|
+
import LogBoxInspectorHeader from './LogBoxInspectorHeader';
|
|
14
|
+
import * as LogBoxStyle from './LogBoxStyle';
|
|
15
|
+
import * as React from 'react';
|
|
16
|
+
-import {useEffect} from 'react';
|
|
17
|
+
+import {useEffect, useState} from 'react';
|
|
18
|
+
+
|
|
19
|
+
+const errorTitleMap = {
|
|
20
|
+
+ warn: '控制台警告',
|
|
21
|
+
+ error: '控制台错误',
|
|
22
|
+
+ fatal: '未捕获错误',
|
|
23
|
+
+ syntax: '语法错误',
|
|
24
|
+
+};
|
|
25
|
+
+
|
|
26
|
+
+const MAX_CLIPBOARD_LENGTH = 12 * 1024;
|
|
27
|
+
+const MAX_MESSAGE_LENGTH = 6000;
|
|
28
|
+
+const MAX_JS_STACK_FRAMES = 30;
|
|
29
|
+
+const MAX_COMPONENT_STACK_FRAMES = 20;
|
|
30
|
+
+const MAX_CODE_FRAME_LINES = 40;
|
|
31
|
+
+
|
|
32
|
+
+function limitText(text: string, maxLength: number): string {
|
|
33
|
+
+ if (text.length <= maxLength) {
|
|
34
|
+
+ return text;
|
|
35
|
+
+ }
|
|
36
|
+
+ return `${text.slice(0, maxLength)}\n... 已省略 ${text.length - maxLength} 个字符 ...`;
|
|
37
|
+
+}
|
|
38
|
+
+
|
|
39
|
+
+function limitItems<T>(items: Array<T>, maxItems: number): Array<T> {
|
|
40
|
+
+ return items.length <= maxItems ? items : items.slice(0, maxItems);
|
|
41
|
+
+}
|
|
42
|
+
+
|
|
43
|
+
+function formatLogForClipboard(log: LogBoxLog): string {
|
|
44
|
+
+ const title = log.message.content.includes('Uncaught (in promise')
|
|
45
|
+
+ ? '未处理的 Promise 拒绝'
|
|
46
|
+
+ : (log.isComponentError
|
|
47
|
+
+ ? '渲染错误'
|
|
48
|
+
+ : (errorTitleMap[log.level] ?? '错误'));
|
|
49
|
+
+ const sections = [
|
|
50
|
+
+ `错误类型:${title}`,
|
|
51
|
+
+ `错误信息:\n${limitText(log.message.content, MAX_MESSAGE_LENGTH)}`,
|
|
52
|
+
+ ];
|
|
53
|
+
+ const stack = log.getAvailableStack();
|
|
54
|
+
+ if (stack.length > 0) {
|
|
55
|
+
+ const visibleStack = limitItems(stack, MAX_JS_STACK_FRAMES);
|
|
56
|
+
+ sections.push(
|
|
57
|
+
+ `JS Stack:\n${visibleStack
|
|
58
|
+
+ .map(frame => {
|
|
59
|
+
+ const location = [frame.file, frame.lineNumber, frame.column]
|
|
60
|
+
+ .filter(value => value != null && value !== '')
|
|
61
|
+
+ .join(':');
|
|
62
|
+
+ return `at ${frame.methodName}${location ? ` (${location})` : ''}`;
|
|
63
|
+
+ })
|
|
64
|
+
+ .join('\n')}${
|
|
65
|
+
+ stack.length > visibleStack.length
|
|
66
|
+
+ ? `\n... 已省略 ${stack.length - visibleStack.length} 个调用栈帧 ...`
|
|
67
|
+
+ : ''
|
|
68
|
+
+ }`,
|
|
69
|
+
+ );
|
|
70
|
+
+ }
|
|
71
|
+
+ const componentStack = log.getAvailableComponentStack();
|
|
72
|
+
+ if (componentStack != null && componentStack.length > 0) {
|
|
73
|
+
+ const visibleComponentStack = limitItems(
|
|
74
|
+
+ componentStack,
|
|
75
|
+
+ MAX_COMPONENT_STACK_FRAMES,
|
|
76
|
+
+ );
|
|
77
|
+
+ sections.push(
|
|
78
|
+
+ `Component Stack:\n${visibleComponentStack
|
|
79
|
+
+ .map(frame => {
|
|
80
|
+
+ const location = frame.location
|
|
81
|
+
+ ? `:${frame.location.row}:${frame.location.column}`
|
|
82
|
+
+ : '';
|
|
83
|
+
+ return `<${frame.content} /> (${frame.fileName}${location})`;
|
|
84
|
+
+ })
|
|
85
|
+
+ .join('\n')}${
|
|
86
|
+
+ componentStack.length > visibleComponentStack.length
|
|
87
|
+
+ ? `\n... 已省略 ${componentStack.length - visibleComponentStack.length} 个组件 ...`
|
|
88
|
+
+ : ''
|
|
89
|
+
+ }`,
|
|
90
|
+
+ );
|
|
91
|
+
+ }
|
|
92
|
+
+ if (log.codeFrame != null) {
|
|
93
|
+
+ const codeLines = log.codeFrame.content.split('\n');
|
|
94
|
+
+ const visibleCode = limitItems(codeLines, MAX_CODE_FRAME_LINES);
|
|
95
|
+
+ sections.push(
|
|
96
|
+
+ `源代码:\n${log.codeFrame.fileName}\n${visibleCode.join('\n')}${
|
|
97
|
+
+ codeLines.length > visibleCode.length
|
|
98
|
+
+ ? `\n... 已省略 ${codeLines.length - visibleCode.length} 行源码 ...`
|
|
99
|
+
+ : ''
|
|
100
|
+
+ }`,
|
|
101
|
+
+ );
|
|
102
|
+
+ }
|
|
103
|
+
+ const formatted = sections.join('\n\n').replace(/\u001b\[[0-9;]*m/g, '');
|
|
104
|
+
+ return formatted.length <= MAX_CLIPBOARD_LENGTH
|
|
105
|
+
+ ? formatted
|
|
106
|
+
+ : `${formatted.slice(0, MAX_CLIPBOARD_LENGTH)}\n... 已截断,复制内容上限为 ${MAX_CLIPBOARD_LENGTH} 字符 ...`;
|
|
107
|
+
+}
|
|
108
|
+
|
|
109
|
+
type Props = $ReadOnly<{
|
|
110
|
+
onDismiss: () => void,
|
|
111
|
+
@@ -32,6 +123,9 @@
|
|
112
|
+
export default function LogBoxInspector(props: Props): React.Node {
|
|
113
|
+
const {logs, selectedIndex} = props;
|
|
114
|
+
let log = logs[selectedIndex];
|
|
115
|
+
+ const [copyStatus, setCopyStatus] = useState<'idle' | 'copied' | 'failed'>(
|
|
116
|
+
+ 'idle',
|
|
117
|
+
+ );
|
|
118
|
+
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (log) {
|
|
121
|
+
@@ -55,8 +149,22 @@
|
|
122
|
+
Keyboard.dismiss();
|
|
123
|
+
}, []);
|
|
124
|
+
|
|
125
|
+
+ useEffect(() => {
|
|
126
|
+
+ setCopyStatus('idle');
|
|
127
|
+
+ }, [log]);
|
|
128
|
+
+
|
|
129
|
+
function _handleRetry() {
|
|
130
|
+
LogBoxData.retrySymbolicateLogNow(log);
|
|
131
|
+
+ }
|
|
132
|
+
+
|
|
133
|
+
+ function _handleCopy() {
|
|
134
|
+
+ try {
|
|
135
|
+
+ Clipboard.setString(formatLogForClipboard(log));
|
|
136
|
+
+ setCopyStatus('copied');
|
|
137
|
+
+ } catch {
|
|
138
|
+
+ // 复制失败不能再次触发 LogBox,否则会遮盖原始错误。
|
|
139
|
+
+ setCopyStatus('failed');
|
|
140
|
+
+ }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (log == null) {
|
|
144
|
+
@@ -73,6 +181,8 @@
|
|
145
|
+
/>
|
|
146
|
+
<LogBoxInspectorBody log={log} onRetry={_handleRetry} />
|
|
147
|
+
<LogBoxInspectorFooter
|
|
148
|
+
+ copyStatus={copyStatus}
|
|
149
|
+
+ onCopy={_handleCopy}
|
|
150
|
+
onDismiss={props.onDismiss}
|
|
151
|
+
onMinimize={props.onMinimize}
|
|
152
|
+
level={log.level}
|
|
153
|
+
diff --git a/Libraries/LogBox/UI/LogBoxInspectorBody.js b/Libraries/LogBox/UI/LogBoxInspectorBody.js
|
|
154
|
+
--- a/Libraries/LogBox/UI/LogBoxInspectorBody.js
|
|
155
|
+
+++ b/Libraries/LogBox/UI/LogBoxInspectorBody.js
|
|
156
|
+
@@ -20,11 +20,11 @@
|
|
157
|
+
import {useEffect, useState} from 'react';
|
|
158
|
+
|
|
159
|
+
const headerTitleMap = {
|
|
160
|
+
- warn: 'Console Warning',
|
|
161
|
+
- error: 'Console Error',
|
|
162
|
+
- fatal: 'Uncaught Error',
|
|
163
|
+
- syntax: 'Syntax Error',
|
|
164
|
+
- component: 'Render Error',
|
|
165
|
+
+ warn: '控制台警告',
|
|
166
|
+
+ error: '控制台错误',
|
|
167
|
+
+ fatal: '未捕获错误',
|
|
168
|
+
+ syntax: '语法错误',
|
|
169
|
+
+ component: '渲染错误',
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export default function LogBoxInspectorBody(props: {
|
|
173
|
+
@@ -37,9 +37,12 @@
|
|
174
|
+
setCollapsed(true);
|
|
175
|
+
}, [props.log]);
|
|
176
|
+
|
|
177
|
+
- const headerTitle =
|
|
178
|
+
- props.log.type ??
|
|
179
|
+
- headerTitleMap[props.log.isComponentError ? 'component' : props.log.level];
|
|
180
|
+
+ const headerTitle = props.log.message.content.includes('Uncaught (in promise')
|
|
181
|
+
+ ? '未处理的 Promise 拒绝'
|
|
182
|
+
+ : (props.log.type === 'Metro Error'
|
|
183
|
+
+ ? '编译错误'
|
|
184
|
+
+ : (props.log.type ??
|
|
185
|
+
+ headerTitleMap[props.log.isComponentError ? 'component' : props.log.level]));
|
|
186
|
+
|
|
187
|
+
if (collapsed) {
|
|
188
|
+
return (
|
|
189
|
+
diff --git a/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.js b/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.js
|
|
190
|
+
--- a/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.js
|
|
191
|
+
+++ b/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.js
|
|
192
|
+
@@ -94,7 +94,7 @@
|
|
193
|
+
}
|
|
194
|
+
return (
|
|
195
|
+
<LogBoxInspectorSection
|
|
196
|
+
- heading={sources.length > 1 ? 'Sources' : 'Source'}
|
|
197
|
+
+ heading="源代码"
|
|
198
|
+
action={<AppInfo />}>
|
|
199
|
+
{sources.map((frame, index) => (
|
|
200
|
+
<CodeFrameDisplay key={index} codeFrame={frame} />
|
|
201
|
+
diff --git a/Libraries/LogBox/UI/LogBoxInspectorFooter.js b/Libraries/LogBox/UI/LogBoxInspectorFooter.js
|
|
202
|
+
--- a/Libraries/LogBox/UI/LogBoxInspectorFooter.js
|
|
203
|
+
+++ b/Libraries/LogBox/UI/LogBoxInspectorFooter.js
|
|
204
|
+
@@ -18,36 +18,52 @@
|
|
205
|
+
import * as React from 'react';
|
|
206
|
+
|
|
207
|
+
type Props = $ReadOnly<{
|
|
208
|
+
+ copyStatus: 'idle' | 'copied' | 'failed',
|
|
209
|
+
+ onCopy: () => void,
|
|
210
|
+
onDismiss: () => void,
|
|
211
|
+
onMinimize: () => void,
|
|
212
|
+
level?: ?LogLevel,
|
|
213
|
+
}>;
|
|
214
|
+
|
|
215
|
+
export default function LogBoxInspectorFooter(props: Props): React.Node {
|
|
216
|
+
- if (props.level === 'syntax') {
|
|
217
|
+
- return (
|
|
218
|
+
- <View style={styles.root}>
|
|
219
|
+
- <View style={styles.button}>
|
|
220
|
+
- <Text id="logbox_dismissable_text" style={styles.syntaxErrorText}>
|
|
221
|
+
- This error cannot be dismissed.
|
|
222
|
+
- </Text>
|
|
223
|
+
- </View>
|
|
224
|
+
- </View>
|
|
225
|
+
- );
|
|
226
|
+
- }
|
|
227
|
+
+ const copyButtonText =
|
|
228
|
+
+ props.copyStatus === 'copied'
|
|
229
|
+
+ ? '已复制'
|
|
230
|
+
+ : props.copyStatus === 'failed'
|
|
231
|
+
+ ? '复制失败'
|
|
232
|
+
+ : '复制错误给智能体修复';
|
|
233
|
+
|
|
234
|
+
return (
|
|
235
|
+
- <View style={styles.root}>
|
|
236
|
+
- <LogBoxInspectorFooterButton
|
|
237
|
+
- id="logbox_footer_button_dismiss"
|
|
238
|
+
- text="Dismiss"
|
|
239
|
+
- onPress={props.onDismiss}
|
|
240
|
+
- />
|
|
241
|
+
- <LogBoxInspectorFooterButton
|
|
242
|
+
- id="logbox_footer_button_minimize"
|
|
243
|
+
- text="Minimize"
|
|
244
|
+
- onPress={props.onMinimize}
|
|
245
|
+
- />
|
|
246
|
+
+ <View>
|
|
247
|
+
+ <View style={styles.root}>
|
|
248
|
+
+ <LogBoxInspectorFooterButton
|
|
249
|
+
+ id="logbox_footer_button_copy"
|
|
250
|
+
+ text={copyButtonText}
|
|
251
|
+
+ onPress={props.onCopy}
|
|
252
|
+
+ />
|
|
253
|
+
+ </View>
|
|
254
|
+
+ <View style={styles.root}>
|
|
255
|
+
+ {props.level === 'syntax' ? (
|
|
256
|
+
+ <View style={styles.button}>
|
|
257
|
+
+ <Text id="logbox_dismissable_text" style={styles.syntaxErrorText}>
|
|
258
|
+
+ 此错误无法关闭,请修复代码后重新加载。
|
|
259
|
+
+ </Text>
|
|
260
|
+
+ </View>
|
|
261
|
+
+ ) : (
|
|
262
|
+
+ <>
|
|
263
|
+
+ <LogBoxInspectorFooterButton
|
|
264
|
+
+ id="logbox_footer_button_dismiss"
|
|
265
|
+
+ text="关闭"
|
|
266
|
+
+ onPress={props.onDismiss}
|
|
267
|
+
+ />
|
|
268
|
+
+ <LogBoxInspectorFooterButton
|
|
269
|
+
+ id="logbox_footer_button_minimize"
|
|
270
|
+
+ text="最小化"
|
|
271
|
+
+ onPress={props.onMinimize}
|
|
272
|
+
+ />
|
|
273
|
+
+ </>
|
|
274
|
+
+ )}
|
|
275
|
+
+ </View>
|
|
276
|
+
</View>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
diff --git a/Libraries/LogBox/UI/LogBoxInspectorHeader.js b/Libraries/LogBox/UI/LogBoxInspectorHeader.js
|
|
280
|
+
--- a/Libraries/LogBox/UI/LogBoxInspectorHeader.js
|
|
281
|
+
+++ b/Libraries/LogBox/UI/LogBoxInspectorHeader.js
|
|
282
|
+
@@ -50,7 +50,7 @@
|
|
283
|
+
const nextIndex =
|
|
284
|
+
props.selectedIndex + 1 > props.total - 1 ? 0 : props.selectedIndex + 1;
|
|
285
|
+
|
|
286
|
+
- const titleText = `Log ${props.selectedIndex + 1} of ${props.total}`;
|
|
287
|
+
+ const titleText = `日志 ${props.selectedIndex + 1} / ${props.total}`;
|
|
288
|
+
|
|
289
|
+
return (
|
|
290
|
+
<LogBoxInspectorHeaderSafeArea style={styles[props.level]}>
|
|
291
|
+
diff --git a/Libraries/LogBox/UI/LogBoxInspectorMessageHeader.js b/Libraries/LogBox/UI/LogBoxInspectorMessageHeader.js
|
|
292
|
+
--- a/Libraries/LogBox/UI/LogBoxInspectorMessageHeader.js
|
|
293
|
+
+++ b/Libraries/LogBox/UI/LogBoxInspectorMessageHeader.js
|
|
294
|
+
@@ -38,7 +38,7 @@
|
|
295
|
+
}
|
|
296
|
+
return (
|
|
297
|
+
<Text style={messageStyles.collapse} onPress={() => props.onPress()}>
|
|
298
|
+
- ... See More
|
|
299
|
+
+ ... 查看更多
|
|
300
|
+
</Text>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
diff --git a/Libraries/LogBox/UI/LogBoxInspectorStackFrames.js b/Libraries/LogBox/UI/LogBoxInspectorStackFrames.js
|
|
304
|
+
--- a/Libraries/LogBox/UI/LogBoxInspectorStackFrames.js
|
|
305
|
+
+++ b/Libraries/LogBox/UI/LogBoxInspectorStackFrames.js
|
|
306
|
+
@@ -34,7 +34,7 @@
|
|
307
|
+
collapsed: boolean,
|
|
308
|
+
): string {
|
|
309
|
+
if (stackFrames.length === 0) {
|
|
310
|
+
- return 'No frames to show';
|
|
311
|
+
+ return '暂无调用栈';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const collapsedCount = stackFrames.reduce((count, {collapse}) => {
|
|
315
|
+
@@ -46,23 +46,12 @@
|
|
316
|
+
}, 0);
|
|
317
|
+
|
|
318
|
+
if (collapsedCount === 0) {
|
|
319
|
+
- return 'Showing all frames';
|
|
320
|
+
- }
|
|
321
|
+
-
|
|
322
|
+
- const framePlural = `frame${collapsedCount > 1 ? 's' : ''}`;
|
|
323
|
+
- if (collapsedCount === stackFrames.length) {
|
|
324
|
+
- return collapsed
|
|
325
|
+
- ? `See${
|
|
326
|
+
- collapsedCount > 1 ? ' all ' : ' '
|
|
327
|
+
- }${collapsedCount} collapsed ${framePlural}`
|
|
328
|
+
- : `Collapse${
|
|
329
|
+
- collapsedCount > 1 ? ' all ' : ' '
|
|
330
|
+
- }${collapsedCount} ${framePlural}`;
|
|
331
|
+
- } else {
|
|
332
|
+
- return collapsed
|
|
333
|
+
- ? `See ${collapsedCount} more ${framePlural}`
|
|
334
|
+
- : `Collapse ${collapsedCount} ${framePlural}`;
|
|
335
|
+
- }
|
|
336
|
+
+ return '已显示全部调用栈';
|
|
337
|
+
+ }
|
|
338
|
+
+
|
|
339
|
+
+ return collapsed
|
|
340
|
+
+ ? `展开 ${collapsedCount} 个调用栈帧`
|
|
341
|
+
+ : `折叠 ${collapsedCount} 个调用栈帧`;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function LogBoxInspectorStackFrames(props: Props): React.Node {
|
|
345
|
+
@@ -85,7 +74,7 @@
|
|
346
|
+
|
|
347
|
+
return (
|
|
348
|
+
<LogBoxInspectorSection
|
|
349
|
+
- heading="Call Stack"
|
|
350
|
+
+ heading="调用栈"
|
|
351
|
+
action={
|
|
352
|
+
<LogBoxInspectorSourceMapStatus
|
|
353
|
+
onPress={
|
|
354
|
+
@@ -97,8 +86,7 @@
|
|
355
|
+
{props.log.symbolicated.status !== 'COMPLETE' && (
|
|
356
|
+
<View style={stackStyles.hintBox}>
|
|
357
|
+
<Text style={stackStyles.hintText}>
|
|
358
|
+
- This call stack is not symbolicated. Some features are unavailable
|
|
359
|
+
- such as viewing the function name or tapping to open files.
|
|
360
|
+
+ 当前调用栈尚未完成符号化,函数名和源码跳转可能不可用。
|
|
361
|
+
</Text>
|
|
362
|
+
</View>
|
|
363
|
+
)}
|
|
364
|
+
diff --git a/Libraries/LogBox/UI/LogBoxInspectorReactFrames.js b/Libraries/LogBox/UI/LogBoxInspectorReactFrames.js
|
|
365
|
+
--- a/Libraries/LogBox/UI/LogBoxInspectorReactFrames.js
|
|
366
|
+
+++ b/Libraries/LogBox/UI/LogBoxInspectorReactFrames.js
|
|
367
|
+
@@ -73,14 +73,14 @@
|
|
368
|
+
|
|
369
|
+
const count = props.log.getAvailableComponentStack().length - 3;
|
|
370
|
+
if (collapsed) {
|
|
371
|
+
- return `See ${count} more components`;
|
|
372
|
+
+ return `查看更多 ${count} 个组件`;
|
|
373
|
+
} else {
|
|
374
|
+
- return `Collapse ${count} components`;
|
|
375
|
+
+ return `折叠 ${count} 个组件`;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return (
|
|
380
|
+
- <LogBoxInspectorSection heading="Component Stack">
|
|
381
|
+
+ <LogBoxInspectorSection heading="组件栈">
|
|
382
|
+
{getStackList().map((frame, index) => (
|
|
383
|
+
<View
|
|
384
|
+
// Unfortunately we don't have a unique identifier for stack traces.
|
package/pnpmfile.cjs
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
const crypto = require('node:crypto');
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
|
|
5
|
+
const MAX_VIRTUAL_STORE_DIR_LENGTH = 50;
|
|
6
|
+
const EXPO_CLI_PATCH_SELECTOR = '@expo/cli@>=55.0.16 <55.0.23';
|
|
7
|
+
const REACT_NATIVE_PATCH_SELECTOR = 'react-native@0.83.2';
|
|
8
|
+
const { blockedPackages = [] } = require('./pnpm-config.json');
|
|
9
|
+
|
|
10
|
+
const managedPatches = {
|
|
11
|
+
[REACT_NATIVE_PATCH_SELECTOR]: {
|
|
12
|
+
filename: 'react-native@0.83.2.patch',
|
|
13
|
+
legacyHashes: new Set([
|
|
14
|
+
'bc87e66a7a5c2e0fbe5d1be5c20d1c6b8456e84ea287a1acb15202264b4f2c37',
|
|
15
|
+
'c03c7638b6393e9876df08f7e9160a9122f00d6e1f1e76e8f4adb4cba3c3b539',
|
|
16
|
+
]),
|
|
17
|
+
},
|
|
18
|
+
[EXPO_CLI_PATCH_SELECTOR]: {
|
|
19
|
+
filename: '@expo__cli@55.0.16-55.0.22.patch',
|
|
20
|
+
legacyHashes: new Set([
|
|
21
|
+
'9a0d662675fba8088396c6cdbab7d36d81fbe9acb3727144a50826ff3500b093',
|
|
22
|
+
]),
|
|
23
|
+
},
|
|
24
|
+
'metro-runtime@0.83.3': {
|
|
25
|
+
filename: 'metro-runtime@0.83.3.patch',
|
|
26
|
+
legacyHashes: new Set(),
|
|
27
|
+
},
|
|
28
|
+
'metro-file-map@0.83.3': {
|
|
29
|
+
filename: 'metro-file-map@0.83.3.patch',
|
|
30
|
+
legacyHashes: new Set(),
|
|
31
|
+
},
|
|
32
|
+
'@shopify/react-native-skia@2.4.18': {
|
|
33
|
+
filename: '@shopify__react-native-skia@2.4.18.patch',
|
|
34
|
+
legacyHashes: new Set([
|
|
35
|
+
'daf0050c82cb2494564e4244a4f5cd06787ee8817b054903f9e1e5eb0d734fa0',
|
|
36
|
+
]),
|
|
37
|
+
},
|
|
38
|
+
'expo-modules-core@55.0.15': {
|
|
39
|
+
filename: 'expo-modules-core@55.0.15.patch',
|
|
40
|
+
legacyHashes: new Set([
|
|
41
|
+
'77aed95955570699b427770e962fe49168c03e900577e28b717afcc56c084dca',
|
|
42
|
+
]),
|
|
43
|
+
},
|
|
44
|
+
'expo@55.0.6': {
|
|
45
|
+
filename: 'expo@55.0.6.patch',
|
|
46
|
+
legacyHashes: new Set([
|
|
47
|
+
'd2507f55976f6c5da8f35d26906f4bd46415e042570afc3bd7c543c86abc5a61',
|
|
48
|
+
'5dda484d5acb6475495587d96c8cbae5bc7514a5781159c92faa9637e6e257ec',
|
|
49
|
+
]),
|
|
50
|
+
},
|
|
51
|
+
'react-native-css-interop@0.2.6': {
|
|
52
|
+
filename: 'react-native-css-interop@0.2.6.patch',
|
|
53
|
+
legacyHashes: new Set([
|
|
54
|
+
'c703e879e4691574bf5e47d743a9a33846b3145149f2398c3bbd6ed835575635',
|
|
55
|
+
]),
|
|
56
|
+
},
|
|
57
|
+
'react-native-reanimated@4.2.1': {
|
|
58
|
+
filename: 'react-native-reanimated@4.2.1.patch',
|
|
59
|
+
legacyHashes: new Set([
|
|
60
|
+
'b2c55038b2fa525ad5e84bd4940ddd2b7298b559de2d0d1709f32ced57e929e9',
|
|
61
|
+
]),
|
|
62
|
+
},
|
|
63
|
+
'react-native-worklets@0.7.2': {
|
|
64
|
+
filename: 'react-native-worklets@0.7.2.patch',
|
|
65
|
+
legacyHashes: new Set([
|
|
66
|
+
'8fee2c39a582a0ec150a3fd5af632fba6736dc03565763d21376b54820fe6668',
|
|
67
|
+
]),
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
function getPatchHash(patchPath) {
|
|
72
|
+
return crypto.createHash('sha256').update(fs.readFileSync(patchPath)).digest('hex');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveProjectPatchPath(config, patchPath) {
|
|
76
|
+
if (path.isAbsolute(patchPath)) return patchPath;
|
|
77
|
+
|
|
78
|
+
return path.resolve(getProjectRoot(config), patchPath);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getProjectRoot(config) {
|
|
82
|
+
return config.lockfileDir || config.dir || process.cwd();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function getBundledPatchPath(config, filename) {
|
|
86
|
+
const configDependencyPath = path.join(
|
|
87
|
+
getProjectRoot(config),
|
|
88
|
+
'node_modules',
|
|
89
|
+
'.pnpm-config',
|
|
90
|
+
'miaoda-expo-devkit',
|
|
91
|
+
'pnpm-patches',
|
|
92
|
+
filename,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if (fs.existsSync(configDependencyPath)) return configDependencyPath;
|
|
96
|
+
return path.join(__dirname, 'pnpm-patches', filename);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function applyManagedPatches(config) {
|
|
100
|
+
const patchedDependencies = { ...config.patchedDependencies };
|
|
101
|
+
|
|
102
|
+
for (const [selector, managedPatch] of Object.entries(managedPatches)) {
|
|
103
|
+
const bundledPatchPath = getBundledPatchPath(config, managedPatch.filename);
|
|
104
|
+
const projectPatch = patchedDependencies[selector];
|
|
105
|
+
|
|
106
|
+
if (projectPatch == null) {
|
|
107
|
+
patchedDependencies[selector] = bundledPatchPath;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const projectPatchPath = resolveProjectPatchPath(config, projectPatch);
|
|
112
|
+
try {
|
|
113
|
+
if (managedPatch.legacyHashes.has(getPatchHash(projectPatchPath))) {
|
|
114
|
+
patchedDependencies[selector] = bundledPatchPath;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
// Keep pnpm's normal missing/unreadable patch error and add migration context below.
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.warn(
|
|
122
|
+
`[miaoda-expo-devkit] Preserving custom patch for ${selector}: ${projectPatch}. ` +
|
|
123
|
+
'It differs from the known template patch and overrides the DevKit patch.',
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const configuredMaxLength = Number.isFinite(config.virtualStoreDirMaxLength)
|
|
128
|
+
? config.virtualStoreDirMaxLength
|
|
129
|
+
: 120;
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
...config,
|
|
133
|
+
allowUnusedPatches: config.allowUnusedPatches ?? true,
|
|
134
|
+
virtualStoreDirMaxLength: Math.min(configuredMaxLength, MAX_VIRTUAL_STORE_DIR_LENGTH),
|
|
135
|
+
patchedDependencies,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function hasManagedExpoCliPatch(version) {
|
|
140
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
|
|
141
|
+
if (match == null) return false;
|
|
142
|
+
|
|
143
|
+
const [, major, minor, patch] = match.map(Number);
|
|
144
|
+
return major === 55 && minor === 0 && patch >= 16 && patch < 23;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function readPackage(pkg, context) {
|
|
148
|
+
if (pkg.name === '@expo/cli' && !hasManagedExpoCliPatch(pkg.version)) {
|
|
149
|
+
console.warn(
|
|
150
|
+
`[miaoda-expo-devkit] No AI error collector patch is registered for @expo/cli@${pkg.version}. ` +
|
|
151
|
+
`The validated range is ${EXPO_CLI_PATCH_SELECTOR}.`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
const blockedPackage = blockedPackages.find(({ name }) => name === pkg.name);
|
|
155
|
+
if (blockedPackage == null) return pkg;
|
|
156
|
+
|
|
157
|
+
context.log(`Blocked installation of ${pkg.name}`);
|
|
158
|
+
const error = new Error(`Restricted: ${blockedPackage.reason}`);
|
|
159
|
+
error.code = 'ERR_PNPM_RESTRICTED_PACKAGE';
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
module.exports = {
|
|
164
|
+
hooks: {
|
|
165
|
+
readPackage,
|
|
166
|
+
updateConfig: applyManagedPatches,
|
|
167
|
+
},
|
|
168
|
+
};
|