@sigmaott/base-next 1.4.36 → 1.4.38
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/package.json +1 -1
- package/src/composables/plan.ts +14 -2
- package/src/plugins/wujie.ts +58 -5
- package/src/utils/component-resolution.ts +177 -0
- package/src/utils/micro.ts +56 -8
package/package.json
CHANGED
package/src/composables/plan.ts
CHANGED
|
@@ -42,6 +42,8 @@ export const useNoCardWarningSub = createGlobalState(() => {
|
|
|
42
42
|
})
|
|
43
43
|
|
|
44
44
|
function openNoCardWarning() {
|
|
45
|
+
console.log('running ?: ', window?.$wujie);
|
|
46
|
+
|
|
45
47
|
if (window?.$wujie?.props?.openNoCardWarning) {
|
|
46
48
|
window.$wujie.props.openNoCardWarning()
|
|
47
49
|
}
|
|
@@ -50,8 +52,18 @@ export const useNoCardWarningSub = createGlobalState(() => {
|
|
|
50
52
|
if (microProps.value?.openNoCardWarning) {
|
|
51
53
|
microProps.value.openNoCardWarning()
|
|
52
54
|
} else {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
try {
|
|
56
|
+
if (window.parent && window.parent !== window && window.parent.openNoCardWarning) {
|
|
57
|
+
window.parent.openNoCardWarning()
|
|
58
|
+
} else {
|
|
59
|
+
const { openNoCardWarning: openGlobalNoCardWarning } = useNoCardWarning()
|
|
60
|
+
openGlobalNoCardWarning()
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.warn('Cannot access parent openNoCardWarning:', error)
|
|
64
|
+
const { openNoCardWarning: openGlobalNoCardWarning } = useNoCardWarning()
|
|
65
|
+
openGlobalNoCardWarning()
|
|
66
|
+
}
|
|
55
67
|
}
|
|
56
68
|
}
|
|
57
69
|
else {
|
package/src/plugins/wujie.ts
CHANGED
|
@@ -1,19 +1,72 @@
|
|
|
1
|
+
import { setupGlobalComponentResolutionHandler, handleComponentResolutionError, isComponentResolutionError } from '../utils/component-resolution'
|
|
2
|
+
|
|
1
3
|
export default defineNuxtPlugin((nuxtApp) => {
|
|
4
|
+
// Setup global error handlers
|
|
5
|
+
setupGlobalComponentResolutionHandler()
|
|
6
|
+
|
|
2
7
|
nuxtApp.hook('app:created', async (app) => {
|
|
3
|
-
await new Promise<void>((resolve) => {
|
|
8
|
+
await new Promise<void>((resolve, reject) => {
|
|
4
9
|
if (window.__POWERED_BY_WUJIE__) {
|
|
5
10
|
window.__WUJIE_MOUNT = () => {
|
|
6
|
-
|
|
7
|
-
|
|
11
|
+
try {
|
|
12
|
+
syncMicroState()
|
|
13
|
+
resolve()
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error('[Wujie] Error during mount:', error)
|
|
16
|
+
if (isComponentResolutionError(error)) {
|
|
17
|
+
handleComponentResolutionError(error, 'wujie-mount')
|
|
18
|
+
}
|
|
19
|
+
reject(error)
|
|
20
|
+
}
|
|
8
21
|
}
|
|
9
22
|
window.__WUJIE_UNMOUNT = () => {
|
|
10
|
-
|
|
23
|
+
try {
|
|
24
|
+
app.unmount()
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('[Wujie] Error during unmount:', error)
|
|
27
|
+
if (isComponentResolutionError(error)) {
|
|
28
|
+
handleComponentResolutionError(error, 'wujie-unmount')
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Add timeout to prevent hanging
|
|
34
|
+
const timeout = setTimeout(() => {
|
|
35
|
+
console.warn('[Wujie] Mount timeout, resolving anyway')
|
|
36
|
+
resolve()
|
|
37
|
+
}, 5000)
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
window.__WUJIE.mount()
|
|
41
|
+
clearTimeout(timeout)
|
|
42
|
+
} catch (error) {
|
|
43
|
+
clearTimeout(timeout)
|
|
44
|
+
console.error('[Wujie] Error mounting app:', error)
|
|
45
|
+
if (isComponentResolutionError(error)) {
|
|
46
|
+
handleComponentResolutionError(error, 'wujie-mount-init')
|
|
47
|
+
}
|
|
48
|
+
reject(error)
|
|
11
49
|
}
|
|
12
|
-
window.__WUJIE.mount()
|
|
13
50
|
}
|
|
14
51
|
else {
|
|
15
52
|
resolve()
|
|
16
53
|
}
|
|
17
54
|
})
|
|
18
55
|
})
|
|
56
|
+
|
|
57
|
+
// Enhanced error handling for component resolution
|
|
58
|
+
nuxtApp.hook('app:error', (error) => {
|
|
59
|
+
if (isComponentResolutionError(error)) {
|
|
60
|
+
handleComponentResolutionError(error, 'nuxt-app-error')
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
// Handle Vue component errors
|
|
65
|
+
nuxtApp.vueApp.config.errorHandler = (error, instance, info) => {
|
|
66
|
+
if (isComponentResolutionError(error)) {
|
|
67
|
+
handleComponentResolutionError(error, 'vue-error-handler')
|
|
68
|
+
} else {
|
|
69
|
+
console.error('[Vue] Error:', error, 'Info:', info)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
19
72
|
})
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for handling component resolution issues in micro-frontend architecture
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface ComponentResolutionError extends Error {
|
|
6
|
+
componentName?: string
|
|
7
|
+
path?: string
|
|
8
|
+
isRecoverable?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Detects if an error is related to component resolution
|
|
13
|
+
*/
|
|
14
|
+
export function isComponentResolutionError(error: any): error is ComponentResolutionError {
|
|
15
|
+
return error?.message?.includes('Couldn\'t resolve component') ||
|
|
16
|
+
error?.message?.includes('Failed to resolve component') ||
|
|
17
|
+
error?.message?.includes('Component not found')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Handles component resolution errors with recovery strategies
|
|
22
|
+
*/
|
|
23
|
+
export function handleComponentResolutionError(error: ComponentResolutionError, context: string = 'unknown') {
|
|
24
|
+
console.warn(`[${context}] Component resolution error detected:`, error)
|
|
25
|
+
|
|
26
|
+
// Extract component name and path from error message
|
|
27
|
+
const componentMatch = error.message?.match(/component "([^"]+)"/)
|
|
28
|
+
const pathMatch = error.message?.match(/at "([^"]+)"/)
|
|
29
|
+
|
|
30
|
+
if (componentMatch) {
|
|
31
|
+
error.componentName = componentMatch[1]
|
|
32
|
+
}
|
|
33
|
+
if (pathMatch) {
|
|
34
|
+
error.path = pathMatch[1]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Determine if error is recoverable
|
|
38
|
+
error.isRecoverable = !error.message?.includes('permanent') &&
|
|
39
|
+
!error.message?.includes('fatal')
|
|
40
|
+
|
|
41
|
+
if (error.isRecoverable) {
|
|
42
|
+
console.log(`[${context}] Attempting recovery for component: ${error.componentName} at ${error.path}`)
|
|
43
|
+
|
|
44
|
+
// Strategy 1: Wait and retry
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
if (window.location) {
|
|
47
|
+
console.log(`[${context}] Reloading page for recovery...`)
|
|
48
|
+
window.location.reload()
|
|
49
|
+
}
|
|
50
|
+
}, 1000)
|
|
51
|
+
} else {
|
|
52
|
+
console.error(`[${context}] Non-recoverable component resolution error:`, error)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return error.isRecoverable
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a retry mechanism for component resolution
|
|
60
|
+
*/
|
|
61
|
+
export function createComponentResolutionRetry(
|
|
62
|
+
maxRetries: number = 3,
|
|
63
|
+
delay: number = 1000
|
|
64
|
+
) {
|
|
65
|
+
let retryCount = 0
|
|
66
|
+
|
|
67
|
+
return function retryComponentResolution<T>(
|
|
68
|
+
operation: () => T | Promise<T>,
|
|
69
|
+
context: string = 'component-resolution'
|
|
70
|
+
): Promise<T> {
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
const attempt = async () => {
|
|
73
|
+
try {
|
|
74
|
+
const result = await operation()
|
|
75
|
+
resolve(result)
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (isComponentResolutionError(error) && retryCount < maxRetries) {
|
|
78
|
+
retryCount++
|
|
79
|
+
console.warn(`[${context}] Retry ${retryCount}/${maxRetries} for component resolution...`)
|
|
80
|
+
|
|
81
|
+
setTimeout(() => {
|
|
82
|
+
attempt()
|
|
83
|
+
}, delay * retryCount)
|
|
84
|
+
} else {
|
|
85
|
+
handleComponentResolutionError(error as ComponentResolutionError, context)
|
|
86
|
+
reject(error)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
attempt()
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Debounced component resolution checker
|
|
98
|
+
*/
|
|
99
|
+
export function createComponentResolutionChecker(
|
|
100
|
+
checkInterval: number = 100,
|
|
101
|
+
maxChecks: number = 50
|
|
102
|
+
) {
|
|
103
|
+
let checkCount = 0
|
|
104
|
+
|
|
105
|
+
return function checkComponentResolution(
|
|
106
|
+
componentName: string,
|
|
107
|
+
context: string = 'component-checker'
|
|
108
|
+
): Promise<boolean> {
|
|
109
|
+
return new Promise((resolve) => {
|
|
110
|
+
const check = () => {
|
|
111
|
+
checkCount++
|
|
112
|
+
|
|
113
|
+
// Check if component exists in DOM
|
|
114
|
+
const componentExists = document.querySelector(`[data-component="${componentName}"]`) ||
|
|
115
|
+
document.querySelector(`[data-v-${componentName}]`) ||
|
|
116
|
+
document.querySelector(`#${componentName}`)
|
|
117
|
+
|
|
118
|
+
if (componentExists) {
|
|
119
|
+
console.log(`[${context}] Component ${componentName} found after ${checkCount} checks`)
|
|
120
|
+
resolve(true)
|
|
121
|
+
} else if (checkCount >= maxChecks) {
|
|
122
|
+
console.warn(`[${context}] Component ${componentName} not found after ${maxChecks} checks`)
|
|
123
|
+
resolve(false)
|
|
124
|
+
} else {
|
|
125
|
+
setTimeout(check, checkInterval)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
check()
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Global error handler for component resolution issues
|
|
136
|
+
*/
|
|
137
|
+
export function setupGlobalComponentResolutionHandler() {
|
|
138
|
+
// Handle unhandled promise rejections
|
|
139
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
140
|
+
if (isComponentResolutionError(event.reason)) {
|
|
141
|
+
console.warn('[Global] Unhandled component resolution error:', event.reason)
|
|
142
|
+
handleComponentResolutionError(event.reason, 'global-unhandled')
|
|
143
|
+
event.preventDefault()
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// Handle general errors
|
|
148
|
+
window.addEventListener('error', (event) => {
|
|
149
|
+
if (isComponentResolutionError(event.error)) {
|
|
150
|
+
console.warn('[Global] Component resolution error:', event.error)
|
|
151
|
+
handleComponentResolutionError(event.error, 'global-error')
|
|
152
|
+
event.preventDefault()
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Vue composable for component resolution error handling
|
|
159
|
+
*/
|
|
160
|
+
export function useComponentResolutionError() {
|
|
161
|
+
const errorHandler = (error: any, context: string = 'vue-component') => {
|
|
162
|
+
if (isComponentResolutionError(error)) {
|
|
163
|
+
return handleComponentResolutionError(error, context)
|
|
164
|
+
}
|
|
165
|
+
return false
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const retryHandler = createComponentResolutionRetry()
|
|
169
|
+
const checker = createComponentResolutionChecker()
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
errorHandler,
|
|
173
|
+
retryHandler,
|
|
174
|
+
checker,
|
|
175
|
+
isComponentResolutionError
|
|
176
|
+
}
|
|
177
|
+
}
|
package/src/utils/micro.ts
CHANGED
|
@@ -74,16 +74,64 @@ const defaultLifecycles: Lifecycles = {
|
|
|
74
74
|
beforeLoad: (appWindow) => {
|
|
75
75
|
console.log(`${appWindow.__WUJIE.id} beforeLoad`)
|
|
76
76
|
console.log('[LOG] ~ file: micro.ts:76 ~ appWindow:', appWindow)
|
|
77
|
-
|
|
77
|
+
try {
|
|
78
|
+
addMicroApp(appWindow)
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error(`[Micro] Error adding micro app ${appWindow.__WUJIE.id}:`, error)
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
beforeMount: (appWindow) => {
|
|
84
|
+
console.log(`${appWindow.__WUJIE.id} beforeMount`)
|
|
85
|
+
// Ensure component resolution is ready
|
|
86
|
+
return new Promise((resolve) => {
|
|
87
|
+
setTimeout(() => {
|
|
88
|
+
resolve()
|
|
89
|
+
}, 100)
|
|
90
|
+
})
|
|
91
|
+
},
|
|
92
|
+
afterMount: (appWindow) => {
|
|
93
|
+
console.log(`${appWindow.__WUJIE.id} afterMount`)
|
|
94
|
+
// Verify component is properly mounted
|
|
95
|
+
try {
|
|
96
|
+
if (appWindow.document && appWindow.document.querySelector('[data-v-app]')) {
|
|
97
|
+
console.log(`${appWindow.__WUJIE.id} component properly mounted`)
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.warn(`${appWindow.__WUJIE.id} component mount verification failed:`, error)
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
beforeUnmount: (appWindow) => {
|
|
104
|
+
console.log(`${appWindow.__WUJIE.id} beforeUnmount`)
|
|
105
|
+
try {
|
|
106
|
+
// Clean up any pending operations
|
|
107
|
+
if (appWindow.__WUJIE?.cleanup) {
|
|
108
|
+
appWindow.__WUJIE.cleanup()
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(`${appWindow.__WUJIE.id} cleanup error:`, error)
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
afterUnmount: (appWindow) => console.log(`${appWindow.__WUJIE.id} afterUnmount`),
|
|
115
|
+
activated: (appWindow) => {
|
|
116
|
+
console.log(`${appWindow.__WUJIE.id} activated`)
|
|
117
|
+
// Ensure component is ready when activated
|
|
118
|
+
return new Promise((resolve) => {
|
|
119
|
+
setTimeout(() => {
|
|
120
|
+
resolve()
|
|
121
|
+
}, 50)
|
|
122
|
+
})
|
|
78
123
|
},
|
|
79
|
-
beforeMount: appWindow => console.log(`${appWindow.__WUJIE.id} beforeMount`),
|
|
80
|
-
afterMount: appWindow => console.log(`${appWindow.__WUJIE.id} afterMount`),
|
|
81
|
-
beforeUnmount: appWindow => console.log(`${appWindow.__WUJIE.id} beforeUnmount `),
|
|
82
|
-
afterUnmount: appWindow => console.log(`${appWindow.__WUJIE.id} afterUnmount`),
|
|
83
|
-
activated: appWindow => console.log(`${appWindow.__WUJIE.id} activated`),
|
|
84
124
|
deactivated: appWindow => console.log(`${appWindow.__WUJIE.id} deactivated`),
|
|
85
|
-
loadError: (url, e) =>
|
|
86
|
-
|
|
125
|
+
loadError: (url, e) => {
|
|
126
|
+
console.error(`[Micro] Load error for ${url}:`, e)
|
|
127
|
+
// Attempt recovery for component resolution errors
|
|
128
|
+
if (e.message?.includes('Couldn\'t resolve component')) {
|
|
129
|
+
console.warn('[Micro] Component resolution error detected, attempting recovery...')
|
|
130
|
+
setTimeout(() => {
|
|
131
|
+
window.location.reload()
|
|
132
|
+
}, 2000)
|
|
133
|
+
}
|
|
134
|
+
},
|
|
87
135
|
}
|
|
88
136
|
|
|
89
137
|
const plugins: Array<plugin> = [
|