@coze-arch/cli 0.0.6 → 0.0.7-alpha.196f15
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/lib/__templates__/nextjs/scripts/build.sh +2 -2
- package/lib/__templates__/nextjs/scripts/dev.sh +1 -1
- package/lib/__templates__/nuxt-vue/scripts/build.sh +1 -1
- package/lib/__templates__/nuxt-vue/scripts/dev.sh +1 -1
- package/lib/__templates__/taro/eslint.config.mjs +139 -80
- package/lib/__templates__/taro/src/app.tsx +2 -0
- package/lib/__templates__/taro/src/components/ui/carousel.tsx +4 -4
- package/lib/__templates__/taro/src/components/ui/sheet.tsx +1 -1
- package/lib/__templates__/taro/src/components/ui/toast.tsx +1 -1
- package/lib/__templates__/taro/src/presets/env.ts +1 -0
- package/lib/__templates__/taro/src/presets/h5-container.tsx +2 -1
- package/lib/__templates__/taro/src/presets/h5-error-boundary.tsx +391 -0
- package/lib/__templates__/taro/src/presets/h5-navbar.tsx +4 -3
- package/lib/__templates__/taro/src/presets/h5-styles.ts +3 -1
- package/lib/__templates__/taro/src/presets/index.tsx +15 -2
- package/lib/__templates__/vite/scripts/build.sh +2 -2
- package/lib/__templates__/vite/scripts/dev.sh +1 -1
- package/lib/cli.js +104 -9
- package/package.json +1 -1
- package/lib/__templates__/taro/components.md +0 -1686
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { View } from '@tarojs/components';
|
|
2
|
+
import { Component, PropsWithChildren, useSyncExternalStore } from 'react';
|
|
3
|
+
import { CircleAlert, Copy, RefreshCw, X } from 'lucide-react-taro';
|
|
4
|
+
import { Badge } from '@/components/ui/badge';
|
|
5
|
+
import { Button } from '@/components/ui/button';
|
|
6
|
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
7
|
+
import { Portal } from '@/components/ui/portal';
|
|
8
|
+
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
9
|
+
import { toast } from '@/components/ui/toast';
|
|
10
|
+
import { cn } from '@/lib/utils';
|
|
11
|
+
import { IS_H5_ENV } from './env';
|
|
12
|
+
|
|
13
|
+
type ErrorState = {
|
|
14
|
+
error: Error | null;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type ErrorBoundaryProps = PropsWithChildren;
|
|
18
|
+
|
|
19
|
+
type ErrorReportOptions = {
|
|
20
|
+
componentStack?: string;
|
|
21
|
+
source?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type OverlayStore = {
|
|
25
|
+
error: Error | null;
|
|
26
|
+
report: string;
|
|
27
|
+
source: string;
|
|
28
|
+
visible: boolean;
|
|
29
|
+
open: boolean;
|
|
30
|
+
timestamp: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const EMPTY_STORE: OverlayStore = {
|
|
34
|
+
error: null,
|
|
35
|
+
report: '',
|
|
36
|
+
source: '',
|
|
37
|
+
visible: false,
|
|
38
|
+
open: false,
|
|
39
|
+
timestamp: '',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const ERROR_ACCENT_COLOR = 'hsl(360, 100%, 45%)';
|
|
43
|
+
|
|
44
|
+
let handlersInstalled = false;
|
|
45
|
+
let overlayStore: OverlayStore = EMPTY_STORE;
|
|
46
|
+
const listeners = new Set<() => void>();
|
|
47
|
+
|
|
48
|
+
const emitChange = () => {
|
|
49
|
+
listeners.forEach(listener => listener());
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const subscribe = (listener: () => void) => {
|
|
53
|
+
listeners.add(listener);
|
|
54
|
+
return () => listeners.delete(listener);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const getSnapshot = () => overlayStore;
|
|
58
|
+
|
|
59
|
+
const setOverlayStore = (nextStore: OverlayStore) => {
|
|
60
|
+
overlayStore = nextStore;
|
|
61
|
+
emitChange();
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const copyText = async (text: string) => {
|
|
65
|
+
if (typeof window === 'undefined') return false;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
if (navigator.clipboard?.writeText) {
|
|
69
|
+
await navigator.clipboard.writeText(text);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.warn('[H5ErrorBoundary] Clipboard API copy failed:', error);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const textarea = document.createElement('textarea');
|
|
78
|
+
textarea.value = text;
|
|
79
|
+
textarea.setAttribute('readonly', 'true');
|
|
80
|
+
textarea.style.position = 'fixed';
|
|
81
|
+
textarea.style.opacity = '0';
|
|
82
|
+
document.body.appendChild(textarea);
|
|
83
|
+
textarea.select();
|
|
84
|
+
const copied = document.execCommand('copy');
|
|
85
|
+
document.body.removeChild(textarea);
|
|
86
|
+
return copied;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.warn('[H5ErrorBoundary] Fallback copy failed:', error);
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const normalizeError = (value: unknown) => {
|
|
94
|
+
if (value instanceof Error) {
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (typeof value === 'string') {
|
|
99
|
+
return new Error(value);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
return new Error(JSON.stringify(value));
|
|
104
|
+
} catch {
|
|
105
|
+
return new Error(String(value));
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const buildErrorReport = (error: Error, options: ErrorReportOptions = {}) => {
|
|
110
|
+
const lines = [
|
|
111
|
+
'[H5 Runtime Error]',
|
|
112
|
+
`Time: ${new Date().toISOString()}`,
|
|
113
|
+
options.source ? `Source: ${options.source}` : '',
|
|
114
|
+
`Name: ${error.name}`,
|
|
115
|
+
`Message: ${error.message}`,
|
|
116
|
+
error.stack ? `Stack:\n${error.stack}` : '',
|
|
117
|
+
options.componentStack ? `Component Stack:\n${options.componentStack}` : '',
|
|
118
|
+
typeof navigator !== 'undefined'
|
|
119
|
+
? `User Agent: ${navigator.userAgent}`
|
|
120
|
+
: '',
|
|
121
|
+
].filter(Boolean);
|
|
122
|
+
|
|
123
|
+
return lines.join('\n\n');
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const setPanelOpen = (open: boolean) => {
|
|
127
|
+
if (!overlayStore.visible) return;
|
|
128
|
+
|
|
129
|
+
setOverlayStore({
|
|
130
|
+
...overlayStore,
|
|
131
|
+
open,
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const showH5ErrorOverlay = (
|
|
136
|
+
input: unknown,
|
|
137
|
+
options: ErrorReportOptions = {},
|
|
138
|
+
) => {
|
|
139
|
+
if (typeof window === 'undefined') {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const error = normalizeError(input);
|
|
144
|
+
const report = buildErrorReport(error, options);
|
|
145
|
+
const timestamp = new Date().toLocaleTimeString('zh-CN', {
|
|
146
|
+
hour: '2-digit',
|
|
147
|
+
minute: '2-digit',
|
|
148
|
+
second: '2-digit',
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
setOverlayStore({
|
|
152
|
+
error,
|
|
153
|
+
report,
|
|
154
|
+
source: options.source || 'runtime',
|
|
155
|
+
timestamp,
|
|
156
|
+
visible: true,
|
|
157
|
+
open: false,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
console.error('[H5ErrorOverlay] Showing error overlay:', error, options);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const handleWindowError = (event: ErrorEvent) => {
|
|
164
|
+
const error =
|
|
165
|
+
event.error || new Error(event.message || 'Unknown H5 runtime error');
|
|
166
|
+
showH5ErrorOverlay(error, { source: 'window.error' });
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
|
|
170
|
+
showH5ErrorOverlay(event.reason, { source: 'window.unhandledrejection' });
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export const initializeH5ErrorHandling = () => {
|
|
174
|
+
if (!IS_H5_ENV || typeof window === 'undefined' || handlersInstalled) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
handlersInstalled = true;
|
|
179
|
+
window.addEventListener('error', handleWindowError);
|
|
180
|
+
window.addEventListener('unhandledrejection', handleUnhandledRejection);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const H5ErrorOverlayHost = () => {
|
|
184
|
+
const store = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
185
|
+
|
|
186
|
+
if (!IS_H5_ENV || !store.visible) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const errorName = store.error?.name || 'Error';
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<Portal>
|
|
194
|
+
<View className="pointer-events-none fixed inset-0 z-[2147483646]">
|
|
195
|
+
<View className="pointer-events-auto fixed bottom-5 left-5">
|
|
196
|
+
<Button
|
|
197
|
+
variant="outline"
|
|
198
|
+
size="icon"
|
|
199
|
+
className={cn(
|
|
200
|
+
'h-11 w-11 rounded-full shadow-md transition-transform',
|
|
201
|
+
)}
|
|
202
|
+
style={{
|
|
203
|
+
backgroundColor: 'hsl(359, 100%, 97%)',
|
|
204
|
+
borderColor: 'hsl(359, 100%, 94%)',
|
|
205
|
+
color: ERROR_ACCENT_COLOR,
|
|
206
|
+
}}
|
|
207
|
+
onClick={() => setPanelOpen(!store.open)}
|
|
208
|
+
>
|
|
209
|
+
<CircleAlert size={22} color={ERROR_ACCENT_COLOR} />
|
|
210
|
+
</Button>
|
|
211
|
+
</View>
|
|
212
|
+
|
|
213
|
+
{store.open && (
|
|
214
|
+
<View className="pointer-events-none fixed inset-0 bg-white bg-opacity-15 supports-[backdrop-filter]:backdrop-blur-md">
|
|
215
|
+
<View className="absolute inset-0 flex items-center justify-center px-4 py-4">
|
|
216
|
+
<View
|
|
217
|
+
className="w-full max-w-md"
|
|
218
|
+
style={{
|
|
219
|
+
width:
|
|
220
|
+
'min(calc(100vw - 32px), var(--h5-phone-width, 390px))',
|
|
221
|
+
height: 'min(calc(100vh - 32px), 900px)',
|
|
222
|
+
}}
|
|
223
|
+
>
|
|
224
|
+
<Card
|
|
225
|
+
className={cn(
|
|
226
|
+
'pointer-events-auto h-full rounded-2xl border border-border bg-background text-foreground shadow-2xl',
|
|
227
|
+
)}
|
|
228
|
+
>
|
|
229
|
+
<View className="relative flex h-full flex-col">
|
|
230
|
+
<CardHeader className="gap-2 p-4 pb-2">
|
|
231
|
+
<View className="flex items-start justify-between gap-3">
|
|
232
|
+
<View className="flex flex-wrap items-center gap-2">
|
|
233
|
+
<Badge
|
|
234
|
+
variant="destructive"
|
|
235
|
+
className="border-none bg-red-500 px-3 py-1 text-xs font-medium text-white"
|
|
236
|
+
>
|
|
237
|
+
Runtime Error
|
|
238
|
+
</Badge>
|
|
239
|
+
<Badge
|
|
240
|
+
variant="outline"
|
|
241
|
+
className="px-3 py-1 text-xs"
|
|
242
|
+
>
|
|
243
|
+
{store.source}
|
|
244
|
+
</Badge>
|
|
245
|
+
</View>
|
|
246
|
+
|
|
247
|
+
<View className="flex shrink-0 items-center gap-1">
|
|
248
|
+
<Button
|
|
249
|
+
variant="ghost"
|
|
250
|
+
size="icon"
|
|
251
|
+
className="h-8 w-8 rounded-full"
|
|
252
|
+
onClick={() => window.location.reload()}
|
|
253
|
+
>
|
|
254
|
+
<RefreshCw size={15} color="inherit" />
|
|
255
|
+
</Button>
|
|
256
|
+
<Button
|
|
257
|
+
variant="ghost"
|
|
258
|
+
size="icon"
|
|
259
|
+
className="h-8 w-8 rounded-full"
|
|
260
|
+
onClick={() => setPanelOpen(false)}
|
|
261
|
+
>
|
|
262
|
+
<X size={17} color="inherit" />
|
|
263
|
+
</Button>
|
|
264
|
+
</View>
|
|
265
|
+
</View>
|
|
266
|
+
|
|
267
|
+
<View className="flex items-center justify-between gap-3">
|
|
268
|
+
<CardTitle className="text-left text-lg">
|
|
269
|
+
{errorName}
|
|
270
|
+
</CardTitle>
|
|
271
|
+
|
|
272
|
+
<Button
|
|
273
|
+
variant="outline"
|
|
274
|
+
size="sm"
|
|
275
|
+
className="shrink-0 rounded-lg"
|
|
276
|
+
onClick={async () => {
|
|
277
|
+
const copied = await copyText(store.report);
|
|
278
|
+
if (copied) {
|
|
279
|
+
toast.success('已复制错误信息', {
|
|
280
|
+
description: '可发送给 Agent 进行自动修复',
|
|
281
|
+
position: 'top-center',
|
|
282
|
+
});
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
toast.warning('复制失败', {
|
|
287
|
+
description: '请直接选中文本后手动复制。',
|
|
288
|
+
position: 'top-center',
|
|
289
|
+
});
|
|
290
|
+
}}
|
|
291
|
+
>
|
|
292
|
+
<Copy size={15} color="inherit" />
|
|
293
|
+
<View>复制错误</View>
|
|
294
|
+
</Button>
|
|
295
|
+
</View>
|
|
296
|
+
</CardHeader>
|
|
297
|
+
|
|
298
|
+
<CardContent className="min-h-0 flex-1 overflow-hidden px-4 pb-4 pt-2">
|
|
299
|
+
<View className="flex h-full min-h-0 flex-col gap-2">
|
|
300
|
+
<View className="flex flex-wrap items-center gap-x-4 gap-y-2 rounded-lg border border-border px-3 py-2 text-sm">
|
|
301
|
+
<View className="flex items-center gap-2">
|
|
302
|
+
<View className="text-muted-foreground">Error</View>
|
|
303
|
+
<View className="font-medium text-foreground">
|
|
304
|
+
{store.error?.name || 'Error'}
|
|
305
|
+
</View>
|
|
306
|
+
</View>
|
|
307
|
+
<View className="h-4 w-px bg-border" />
|
|
308
|
+
<View className="flex items-center gap-2">
|
|
309
|
+
<View className="text-muted-foreground">
|
|
310
|
+
Source
|
|
311
|
+
</View>
|
|
312
|
+
<View className="font-medium text-foreground">
|
|
313
|
+
{store.source}
|
|
314
|
+
</View>
|
|
315
|
+
</View>
|
|
316
|
+
</View>
|
|
317
|
+
|
|
318
|
+
<View className="min-h-0 flex flex-1 flex-col overflow-hidden rounded-xl border border-border bg-black text-white">
|
|
319
|
+
<View className="flex items-center justify-between border-b border-white border-opacity-10 px-3 py-3">
|
|
320
|
+
<View className="text-xs font-medium uppercase tracking-wide text-zinc-400">
|
|
321
|
+
Full Report
|
|
322
|
+
</View>
|
|
323
|
+
<Badge
|
|
324
|
+
variant="outline"
|
|
325
|
+
className="border-zinc-700 bg-transparent px-2 py-1 text-xs text-zinc-400"
|
|
326
|
+
>
|
|
327
|
+
{store.timestamp}
|
|
328
|
+
</Badge>
|
|
329
|
+
</View>
|
|
330
|
+
|
|
331
|
+
<ScrollArea
|
|
332
|
+
className="min-h-0 flex-1 w-full"
|
|
333
|
+
orientation="both"
|
|
334
|
+
>
|
|
335
|
+
<View className="inline-block min-w-full whitespace-pre px-3 py-3 pb-8 font-mono text-xs leading-6 text-zinc-200">
|
|
336
|
+
{store.report}
|
|
337
|
+
</View>
|
|
338
|
+
</ScrollArea>
|
|
339
|
+
</View>
|
|
340
|
+
</View>
|
|
341
|
+
</CardContent>
|
|
342
|
+
</View>
|
|
343
|
+
</Card>
|
|
344
|
+
</View>
|
|
345
|
+
</View>
|
|
346
|
+
</View>
|
|
347
|
+
)}
|
|
348
|
+
</View>
|
|
349
|
+
</Portal>
|
|
350
|
+
);
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
class H5ErrorBoundaryInner extends Component<ErrorBoundaryProps, ErrorState> {
|
|
354
|
+
static getDerivedStateFromError(error: Error): Partial<ErrorState> {
|
|
355
|
+
return { error };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
state: ErrorState = {
|
|
359
|
+
error: null,
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
componentDidUpdate(prevProps: ErrorBoundaryProps) {
|
|
363
|
+
if (this.state.error && prevProps.children !== this.props.children) {
|
|
364
|
+
this.setState({ error: null });
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
componentDidCatch(error: Error, info: { componentStack: string }) {
|
|
369
|
+
showH5ErrorOverlay(error, {
|
|
370
|
+
source: 'React Error Boundary',
|
|
371
|
+
componentStack: info.componentStack || '',
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
render() {
|
|
376
|
+
return (
|
|
377
|
+
<>
|
|
378
|
+
<H5ErrorOverlayHost />
|
|
379
|
+
{this.state.error ? null : this.props.children}
|
|
380
|
+
</>
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export const H5ErrorBoundary = ({ children }: PropsWithChildren) => {
|
|
386
|
+
if (!IS_H5_ENV) {
|
|
387
|
+
return <>{children}</>;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return <H5ErrorBoundaryInner>{children}</H5ErrorBoundaryInner>;
|
|
391
|
+
};
|
|
@@ -2,6 +2,7 @@ import { View, Text } from '@tarojs/components';
|
|
|
2
2
|
import Taro, { useDidShow, usePageScroll } from '@tarojs/taro';
|
|
3
3
|
import { useState, useEffect, useCallback } from 'react';
|
|
4
4
|
import { ChevronLeft, House } from 'lucide-react-taro';
|
|
5
|
+
import { IS_H5_ENV } from './env';
|
|
5
6
|
|
|
6
7
|
interface NavConfig {
|
|
7
8
|
navigationBarTitleText?: string;
|
|
@@ -144,7 +145,7 @@ export const H5NavBar = () => {
|
|
|
144
145
|
});
|
|
145
146
|
|
|
146
147
|
useEffect(() => {
|
|
147
|
-
if (
|
|
148
|
+
if (!IS_H5_ENV) return;
|
|
148
149
|
|
|
149
150
|
let timer: NodeJS.Timeout | null = null;
|
|
150
151
|
const observer = new MutationObserver(() => {
|
|
@@ -169,10 +170,10 @@ export const H5NavBar = () => {
|
|
|
169
170
|
}, [updateNavState]);
|
|
170
171
|
|
|
171
172
|
const shouldRender =
|
|
172
|
-
|
|
173
|
+
IS_H5_ENV && navState.visible && navState.navStyle !== 'custom';
|
|
173
174
|
|
|
174
175
|
useEffect(() => {
|
|
175
|
-
if (
|
|
176
|
+
if (!IS_H5_ENV) return;
|
|
176
177
|
if (shouldRender) {
|
|
177
178
|
document.body.classList.add('h5-navbar-visible');
|
|
178
179
|
} else {
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* 如无必要,请勿修改本文件
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { IS_H5_ENV } from './env';
|
|
7
|
+
|
|
6
8
|
const H5_BASE_STYLES = `
|
|
7
9
|
/* H5 端隐藏 TabBar 空图标(只隐藏没有 src 的图标) */
|
|
8
10
|
.weui-tabbar__icon:not([src]),
|
|
@@ -213,7 +215,7 @@ function setupTabbarDetection() {
|
|
|
213
215
|
}
|
|
214
216
|
|
|
215
217
|
export function injectH5Styles() {
|
|
216
|
-
if (
|
|
218
|
+
if (!IS_H5_ENV) return;
|
|
217
219
|
|
|
218
220
|
injectStyles();
|
|
219
221
|
setupTabbarDetection();
|
|
@@ -3,15 +3,28 @@ import { PropsWithChildren } from 'react';
|
|
|
3
3
|
import { injectH5Styles } from './h5-styles';
|
|
4
4
|
import { devDebug } from './dev-debug';
|
|
5
5
|
import { H5Container } from './h5-container';
|
|
6
|
+
import {
|
|
7
|
+
H5ErrorBoundary,
|
|
8
|
+
initializeH5ErrorHandling,
|
|
9
|
+
} from './h5-error-boundary';
|
|
10
|
+
import { IS_H5_ENV } from './env';
|
|
6
11
|
|
|
7
12
|
export const Preset = ({ children }: PropsWithChildren) => {
|
|
13
|
+
if (IS_H5_ENV) {
|
|
14
|
+
initializeH5ErrorHandling();
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
useLaunch(() => {
|
|
9
18
|
devDebug();
|
|
10
19
|
injectH5Styles();
|
|
11
20
|
});
|
|
12
21
|
|
|
13
|
-
if (
|
|
14
|
-
return
|
|
22
|
+
if (IS_H5_ENV) {
|
|
23
|
+
return (
|
|
24
|
+
<H5ErrorBoundary>
|
|
25
|
+
<H5Container>{children}</H5Container>
|
|
26
|
+
</H5ErrorBoundary>
|
|
27
|
+
);
|
|
15
28
|
}
|
|
16
29
|
|
|
17
30
|
return <>{children}</>;
|
|
@@ -9,9 +9,9 @@ echo "Installing dependencies..."
|
|
|
9
9
|
pnpm install --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
|
|
10
10
|
|
|
11
11
|
echo "Building frontend with Vite..."
|
|
12
|
-
|
|
12
|
+
pnpm vite build
|
|
13
13
|
|
|
14
14
|
echo "Bundling server with tsup..."
|
|
15
|
-
|
|
15
|
+
pnpm tsup server/server.ts --format cjs --platform node --target node20 --outDir dist-server --no-splitting --no-minify --external vite
|
|
16
16
|
|
|
17
17
|
echo "Build completed successfully!"
|
package/lib/cli.js
CHANGED
|
@@ -2105,7 +2105,7 @@ const EventBuilder = {
|
|
|
2105
2105
|
};
|
|
2106
2106
|
|
|
2107
2107
|
var name = "@coze-arch/cli";
|
|
2108
|
-
var version = "0.0.
|
|
2108
|
+
var version = "0.0.7-alpha.196f15";
|
|
2109
2109
|
var description = "coze coding devtools cli";
|
|
2110
2110
|
var license = "MIT";
|
|
2111
2111
|
var author = "fanwenjie.fe@bytedance.com";
|
|
@@ -2332,6 +2332,37 @@ const reportError = safeRun(
|
|
|
2332
2332
|
},
|
|
2333
2333
|
);
|
|
2334
2334
|
|
|
2335
|
+
/**
|
|
2336
|
+
* 上报 pnpm install 执行情况
|
|
2337
|
+
*/
|
|
2338
|
+
const reportPnpmInstall = safeRun(
|
|
2339
|
+
'reportPnpmInstall',
|
|
2340
|
+
(params
|
|
2341
|
+
|
|
2342
|
+
|
|
2343
|
+
|
|
2344
|
+
|
|
2345
|
+
|
|
2346
|
+
|
|
2347
|
+
|
|
2348
|
+
) => {
|
|
2349
|
+
reporter.sendEvent(
|
|
2350
|
+
'cli_pnpm_install',
|
|
2351
|
+
{
|
|
2352
|
+
duration: params.duration,
|
|
2353
|
+
success: params.success ? 1 : 0,
|
|
2354
|
+
},
|
|
2355
|
+
{
|
|
2356
|
+
template: params.template,
|
|
2357
|
+
skipped: String(params.skipped),
|
|
2358
|
+
hasPackageJson: String(params.hasPackageJson),
|
|
2359
|
+
...(params.errorCode && { errorCode: String(params.errorCode) }),
|
|
2360
|
+
...(params.errorType && { errorType: params.errorType }),
|
|
2361
|
+
},
|
|
2362
|
+
);
|
|
2363
|
+
},
|
|
2364
|
+
);
|
|
2365
|
+
|
|
2335
2366
|
/**
|
|
2336
2367
|
* 立即上报(在 CLI 退出前调用)
|
|
2337
2368
|
* 等待 500ms 确保设置请求完成和事件发送
|
|
@@ -3684,6 +3715,8 @@ const executeRun = async (
|
|
|
3684
3715
|
options = {},
|
|
3685
3716
|
) => {
|
|
3686
3717
|
const cmdStartTime = Date.now();
|
|
3718
|
+
let fixDuration = 0;
|
|
3719
|
+
let buildStartTime = 0;
|
|
3687
3720
|
|
|
3688
3721
|
try {
|
|
3689
3722
|
logger.info(`Running ${commandName} command...`);
|
|
@@ -3694,15 +3727,20 @@ const executeRun = async (
|
|
|
3694
3727
|
// 1. 对于 build 命令,先执行 fix 以确保项目配置正确
|
|
3695
3728
|
if (['dev', 'build'].includes(commandName)) {
|
|
3696
3729
|
logger.info('\n🔧 Running fix command before build...\n');
|
|
3730
|
+
const fixStartTime = Date.now();
|
|
3697
3731
|
try {
|
|
3698
3732
|
await executeFix();
|
|
3699
3733
|
// eslint-disable-next-line @coze-arch/no-empty-catch
|
|
3700
3734
|
} catch (e) {
|
|
3701
3735
|
// just ignore
|
|
3702
3736
|
}
|
|
3737
|
+
fixDuration = Date.now() - fixStartTime;
|
|
3703
3738
|
logger.info('');
|
|
3704
3739
|
}
|
|
3705
3740
|
|
|
3741
|
+
// 记录实际 build/dev/start 开始时间
|
|
3742
|
+
buildStartTime = Date.now();
|
|
3743
|
+
|
|
3706
3744
|
// 2. 加载 .coze 配置
|
|
3707
3745
|
const config = await loadCozeConfig();
|
|
3708
3746
|
const commandArgs = getCommandConfig(config, commandName);
|
|
@@ -3741,6 +3779,8 @@ const executeRun = async (
|
|
|
3741
3779
|
childProcess.on('close', (code, signal) => {
|
|
3742
3780
|
logStream.end();
|
|
3743
3781
|
|
|
3782
|
+
const buildDuration = Date.now() - buildStartTime;
|
|
3783
|
+
|
|
3744
3784
|
if (code !== 0) {
|
|
3745
3785
|
const errorMessage = `Command exited with code ${_nullishCoalesce$1(code, () => ( 'unknown'))}${signal ? ` and signal ${signal}` : ''}`;
|
|
3746
3786
|
logger.error(errorMessage);
|
|
@@ -3757,6 +3797,12 @@ const executeRun = async (
|
|
|
3757
3797
|
args: JSON.stringify(options),
|
|
3758
3798
|
errorCode: _nullishCoalesce$1(code, () => ( 1)),
|
|
3759
3799
|
errorMessage,
|
|
3800
|
+
categories: {
|
|
3801
|
+
fixDuration: String(fixDuration),
|
|
3802
|
+
buildDuration: String(buildDuration),
|
|
3803
|
+
exitCode: String(_nullishCoalesce$1(code, () => ( 'unknown'))),
|
|
3804
|
+
...(signal && { signal }),
|
|
3805
|
+
},
|
|
3760
3806
|
});
|
|
3761
3807
|
flushSlardar()
|
|
3762
3808
|
.then(() => {
|
|
@@ -3773,6 +3819,11 @@ const executeRun = async (
|
|
|
3773
3819
|
// 上报命令成功
|
|
3774
3820
|
reportCommandComplete(commandName, true, Date.now() - cmdStartTime, {
|
|
3775
3821
|
args: JSON.stringify(options),
|
|
3822
|
+
categories: {
|
|
3823
|
+
fixDuration: String(fixDuration),
|
|
3824
|
+
buildDuration: String(buildDuration),
|
|
3825
|
+
exitCode: '0',
|
|
3826
|
+
},
|
|
3776
3827
|
});
|
|
3777
3828
|
// flush 由 main 函数统一处理
|
|
3778
3829
|
}
|
|
@@ -3786,6 +3837,8 @@ const executeRun = async (
|
|
|
3786
3837
|
}
|
|
3787
3838
|
logStream.end();
|
|
3788
3839
|
|
|
3840
|
+
const buildDuration = Date.now() - buildStartTime;
|
|
3841
|
+
|
|
3789
3842
|
// 上报错误
|
|
3790
3843
|
reportError(error, {
|
|
3791
3844
|
command: commandName,
|
|
@@ -3795,6 +3848,11 @@ const executeRun = async (
|
|
|
3795
3848
|
args: JSON.stringify(options),
|
|
3796
3849
|
errorCode: 1,
|
|
3797
3850
|
errorMessage: error.message,
|
|
3851
|
+
categories: {
|
|
3852
|
+
fixDuration: String(fixDuration),
|
|
3853
|
+
buildDuration: String(buildDuration),
|
|
3854
|
+
errorType: 'child_process_error',
|
|
3855
|
+
},
|
|
3798
3856
|
});
|
|
3799
3857
|
flushSlardar()
|
|
3800
3858
|
.then(() => {
|
|
@@ -3810,6 +3868,8 @@ const executeRun = async (
|
|
|
3810
3868
|
logger.error(`Failed to run ${commandName} command:`);
|
|
3811
3869
|
logger.error(err.message);
|
|
3812
3870
|
|
|
3871
|
+
const buildDuration = buildStartTime > 0 ? Date.now() - buildStartTime : 0;
|
|
3872
|
+
|
|
3813
3873
|
// 上报错误
|
|
3814
3874
|
reportError(err, {
|
|
3815
3875
|
command: commandName,
|
|
@@ -3819,6 +3879,11 @@ const executeRun = async (
|
|
|
3819
3879
|
args: JSON.stringify(options),
|
|
3820
3880
|
errorCode: 1,
|
|
3821
3881
|
errorMessage: err.message,
|
|
3882
|
+
categories: {
|
|
3883
|
+
fixDuration: String(fixDuration),
|
|
3884
|
+
buildDuration: String(buildDuration),
|
|
3885
|
+
errorType: 'execution_error',
|
|
3886
|
+
},
|
|
3822
3887
|
});
|
|
3823
3888
|
flushSlardar()
|
|
3824
3889
|
.then(() => {
|
|
@@ -4575,6 +4640,10 @@ const parseAndValidateParams = (
|
|
|
4575
4640
|
'skip-git',
|
|
4576
4641
|
'skipDev',
|
|
4577
4642
|
'skip-dev',
|
|
4643
|
+
'skipCommit',
|
|
4644
|
+
'skip-commit',
|
|
4645
|
+
'force',
|
|
4646
|
+
'f',
|
|
4578
4647
|
]);
|
|
4579
4648
|
const userParams = parsePassThroughParams(command, knownOptions);
|
|
4580
4649
|
|
|
@@ -4934,15 +5003,41 @@ const installDependenciesStep = ctx => {
|
|
|
4934
5003
|
|
|
4935
5004
|
const { skipInstall } = ctx.options;
|
|
4936
5005
|
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
5006
|
+
// 准备埋点数据
|
|
5007
|
+
const installStartTime = Date.now();
|
|
5008
|
+
let installSuccess = true;
|
|
5009
|
+
let installErrorCode;
|
|
5010
|
+
let installErrorType;
|
|
5011
|
+
|
|
5012
|
+
try {
|
|
5013
|
+
if (!skipInstall) {
|
|
5014
|
+
if (ctx.hasPackageJson) {
|
|
5015
|
+
runPnpmInstall(ctx.absoluteOutputPath);
|
|
5016
|
+
ctx.timer.logPhase('Dependencies installation');
|
|
5017
|
+
} else {
|
|
5018
|
+
logger.info(
|
|
5019
|
+
'\n💡 No package.json found, skipping dependency installation',
|
|
5020
|
+
);
|
|
5021
|
+
}
|
|
4945
5022
|
}
|
|
5023
|
+
} catch (error) {
|
|
5024
|
+
// 捕获 pnpm install 失败的情况
|
|
5025
|
+
installSuccess = false;
|
|
5026
|
+
installErrorCode = 1;
|
|
5027
|
+
installErrorType = 'execution_failed';
|
|
5028
|
+
throw error; // 继续抛出错误,保持原有流程
|
|
5029
|
+
} finally {
|
|
5030
|
+
// 无论成功失败都上报埋点
|
|
5031
|
+
const installDuration = Date.now() - installStartTime;
|
|
5032
|
+
reportPnpmInstall({
|
|
5033
|
+
duration: installDuration,
|
|
5034
|
+
success: installSuccess,
|
|
5035
|
+
template: ctx.templateName,
|
|
5036
|
+
skipped: !!skipInstall,
|
|
5037
|
+
hasPackageJson: !!ctx.hasPackageJson,
|
|
5038
|
+
errorCode: installErrorCode,
|
|
5039
|
+
errorType: installErrorType,
|
|
5040
|
+
});
|
|
4946
5041
|
}
|
|
4947
5042
|
};
|
|
4948
5043
|
|