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.
Files changed (42) hide show
  1. package/README.md +154 -110
  2. package/dist/babel/plugin-lucide-react-native.js +184 -184
  3. package/dist/cli/lint.js +317 -309
  4. package/dist/index.d.mts +1 -110
  5. package/dist/index.d.ts +1 -110
  6. package/dist/index.js +0 -159
  7. package/dist/index.mjs +0 -132
  8. package/dist/metro.d.mts +25 -5
  9. package/dist/metro.d.ts +25 -5
  10. package/dist/metro.js +49 -18
  11. package/dist/metro.mjs +50 -20
  12. package/dist/rules/no-expo-audio-compat.js +2041 -0
  13. package/dist/rules/no-expo-video-compat.js +90 -90
  14. package/dist/stubs/entry-inject.js +0 -2
  15. package/dist/stubs/expo-native-wechat-stub.js +116 -0
  16. package/dist/stubs/expo-router-entry-native-stub.js +4 -0
  17. package/dist/stubs/i18n/en.js +16 -1
  18. package/dist/stubs/i18n/zh.js +16 -1
  19. package/dist/stubs/logbox-control.js +9 -0
  20. package/dist/stubs/screenshot-control.js +1 -1
  21. package/dist/stubs/sentry-react-native-stub.js +15 -180
  22. package/oxlint-config.json +11 -4
  23. package/package.json +60 -5
  24. package/pnpm-patches/@expo__cli@55.0.16-55.0.22.patch +627 -0
  25. package/pnpm-patches/@expo__cli@55.0.16.patch +218 -0
  26. package/pnpm-patches/@expo__cli@55.0.17.patch +218 -0
  27. package/pnpm-patches/@expo__cli@55.0.18.patch +218 -0
  28. package/pnpm-patches/@expo__cli@55.0.19.patch +218 -0
  29. package/pnpm-patches/@expo__cli@55.0.20.patch +218 -0
  30. package/pnpm-patches/@expo__cli@55.0.21.patch +218 -0
  31. package/pnpm-patches/@expo__cli@55.0.22.patch +218 -0
  32. package/pnpm-patches/@shopify__react-native-skia@2.4.18.patch +14 -0
  33. package/pnpm-patches/expo-modules-core@55.0.15.patch +21 -0
  34. package/pnpm-patches/expo@55.0.6.patch +235 -0
  35. package/pnpm-patches/metro-file-map@0.83.3.patch +24 -0
  36. package/pnpm-patches/metro-runtime@0.83.3.patch +13 -0
  37. package/pnpm-patches/react-native-css-interop@0.2.6.patch +12 -0
  38. package/pnpm-patches/react-native-reanimated@4.2.1.patch +53 -0
  39. package/pnpm-patches/react-native-worklets@0.7.2.patch +91 -0
  40. package/pnpm-patches/react-native@0.83.2.patch +384 -0
  41. package/pnpmfile.cjs +168 -0
  42. 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
+ };