@meethive/vite 0.0.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 (72) hide show
  1. package/dist/index.d.ts +4 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +2118 -0
  4. package/dist/index.mjs +2096 -0
  5. package/dist/src/federation/src/dev/expose-development.d.ts +5 -0
  6. package/dist/src/federation/src/dev/expose-development.d.ts.map +1 -0
  7. package/dist/src/federation/src/dev/remote-development.d.ts +5 -0
  8. package/dist/src/federation/src/dev/remote-development.d.ts.map +1 -0
  9. package/dist/src/federation/src/dev/shared-development.d.ts +5 -0
  10. package/dist/src/federation/src/dev/shared-development.d.ts.map +1 -0
  11. package/dist/src/federation/src/index.d.ts +7 -0
  12. package/dist/src/federation/src/index.d.ts.map +1 -0
  13. package/dist/src/federation/src/prod/expose-production.d.ts +5 -0
  14. package/dist/src/federation/src/prod/expose-production.d.ts.map +1 -0
  15. package/dist/src/federation/src/prod/remote-production.d.ts +7 -0
  16. package/dist/src/federation/src/prod/remote-production.d.ts.map +1 -0
  17. package/dist/src/federation/src/prod/shared-production.d.ts +5 -0
  18. package/dist/src/federation/src/prod/shared-production.d.ts.map +1 -0
  19. package/dist/src/federation/src/public.d.ts +40 -0
  20. package/dist/src/federation/src/public.d.ts.map +1 -0
  21. package/dist/src/federation/src/runtime/dynamic-remote.d.ts +79 -0
  22. package/dist/src/federation/src/runtime/dynamic-remote.d.ts.map +1 -0
  23. package/dist/src/federation/src/utils/html.d.ts +12 -0
  24. package/dist/src/federation/src/utils/html.d.ts.map +1 -0
  25. package/dist/src/federation/src/utils/index.d.ts +29 -0
  26. package/dist/src/federation/src/utils/index.d.ts.map +1 -0
  27. package/dist/src/federation/src/utils/semver/compare.d.ts +10 -0
  28. package/dist/src/federation/src/utils/semver/compare.d.ts.map +1 -0
  29. package/dist/src/federation/src/utils/semver/constants.d.ts +11 -0
  30. package/dist/src/federation/src/utils/semver/constants.d.ts.map +1 -0
  31. package/dist/src/federation/src/utils/semver/parser.d.ts +10 -0
  32. package/dist/src/federation/src/utils/semver/parser.d.ts.map +1 -0
  33. package/dist/src/federation/src/utils/semver/satisfy.d.ts +2 -0
  34. package/dist/src/federation/src/utils/semver/satisfy.d.ts.map +1 -0
  35. package/dist/src/federation/src/utils/semver/utils.d.ts +12 -0
  36. package/dist/src/federation/src/utils/semver/utils.d.ts.map +1 -0
  37. package/dist/src/monaco-editor/index.d.ts +35 -0
  38. package/dist/src/monaco-editor/index.d.ts.map +1 -0
  39. package/dist/src/monaco-editor/languageWork.d.ts +10 -0
  40. package/dist/src/monaco-editor/languageWork.d.ts.map +1 -0
  41. package/dist/src/monaco-editor/workerMiddleware.d.ts +9 -0
  42. package/dist/src/monaco-editor/workerMiddleware.d.ts.map +1 -0
  43. package/dist/src/sharp/index.d.ts +12 -0
  44. package/dist/src/sharp/index.d.ts.map +1 -0
  45. package/index.ts +3 -0
  46. package/package.json +48 -0
  47. package/src/federation/src/dev/expose-development.ts +29 -0
  48. package/src/federation/src/dev/remote-development.ts +435 -0
  49. package/src/federation/src/dev/shared-development.ts +29 -0
  50. package/src/federation/src/index.ts +242 -0
  51. package/src/federation/src/prod/expose-production.ts +333 -0
  52. package/src/federation/src/prod/federation_fn_import.js +75 -0
  53. package/src/federation/src/prod/remote-production.ts +658 -0
  54. package/src/federation/src/prod/shared-production.ts +268 -0
  55. package/src/federation/src/public.ts +54 -0
  56. package/src/federation/src/runtime/dynamic-remote.ts +247 -0
  57. package/src/federation/src/utils/html.ts +165 -0
  58. package/src/federation/src/utils/index.ts +255 -0
  59. package/src/federation/src/utils/semver/compare.ts +131 -0
  60. package/src/federation/src/utils/semver/constants.ts +46 -0
  61. package/src/federation/src/utils/semver/parser.ts +253 -0
  62. package/src/federation/src/utils/semver/satisfy.ts +151 -0
  63. package/src/federation/src/utils/semver/utils.ts +93 -0
  64. package/src/federation/types/dynamic-remote.d.ts +105 -0
  65. package/src/federation/types/index.d.ts +344 -0
  66. package/src/federation/types/pluginHooks.d.ts +4 -0
  67. package/src/federation/types/virtual-modules.d.ts +48 -0
  68. package/src/federation/types/viteDevServer.d.ts +22 -0
  69. package/src/monaco-editor/index.ts +205 -0
  70. package/src/monaco-editor/languageWork.ts +36 -0
  71. package/src/monaco-editor/workerMiddleware.ts +78 -0
  72. package/src/sharp/index.ts +93 -0
@@ -0,0 +1,268 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 Origin.js and others.
3
+ //
4
+ // This program and the accompanying materials are licensed under Mulan PSL v2.
5
+ // You can use this software according to the terms and conditions of the Mulan PSL v2.
6
+ // You may obtain a copy of Mulan PSL v2 at:
7
+ // http://license.coscl.org.cn/MulanPSL2
8
+ // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
9
+ // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
10
+ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
11
+ // See the Mulan PSL v2 for more details.
12
+ //
13
+ // SPDX-License-Identifier: MulanPSL-2.0
14
+ // *****************************************************************************
15
+
16
+ import type { PluginHooks } from '../../types/pluginHooks'
17
+ import { NAME_CHAR_REG, parseSharedOptions, removeNonRegLetter } from '../utils'
18
+ import { parsedOptions } from '../public'
19
+ import type { ConfigTypeSet, VitePluginFederationOptions } from '../../types'
20
+ import { basename, join, resolve } from 'path'
21
+ import { readdirSync, readFileSync, statSync } from 'fs'
22
+ const sharedFilePathReg = /__federation_shared_(.+)-.{8}\.js$/
23
+
24
+ // @ts-ignore
25
+ import federation_fn_import from './federation_fn_import.js?raw'
26
+
27
+ export function prodSharedPlugin(
28
+ options: VitePluginFederationOptions
29
+ ): PluginHooks {
30
+ parsedOptions.prodShared = parseSharedOptions(options)
31
+ const shareName2Prop = new Map<string, any>()
32
+ parsedOptions.prodShared.forEach((value) =>
33
+ shareName2Prop.set(removeNonRegLetter(value[0], NAME_CHAR_REG), value[1])
34
+ )
35
+ let isHost
36
+ let isRemote
37
+ const id2Prop = new Map<string, any>()
38
+
39
+
40
+ function negotiateManualChunksCompatibility(inputOptions: any) {
41
+ if (!inputOptions.output) return
42
+
43
+ const outputs = Array.isArray(inputOptions.output)
44
+ ? inputOptions.output
45
+ : [inputOptions.output]
46
+
47
+ outputs.forEach(output => {
48
+ if (!output.manualChunks) return
49
+
50
+ const sharedModuleNames = [...shareName2Prop.keys()]
51
+ const conflictModules = new Set<string>()
52
+
53
+ // 检测冲突模块
54
+ if (typeof output.manualChunks === 'object') {
55
+ Object.values(output.manualChunks).flat().forEach((mod:any) => {
56
+ if (sharedModuleNames.some(shared => mod.includes(shared) || mod === shared)) {
57
+ conflictModules.add(mod)
58
+ }
59
+ })
60
+ }
61
+
62
+ if (conflictModules.size > 0) {
63
+ console.info(
64
+ `[Federation] 检测到模块 [${Array.from(conflictModules).join(', ')}] ` +
65
+ `同时配置在 shared 和 manualChunks 中,启用协商共存模式`
66
+ )
67
+
68
+ // 协商策略:修改 shared 配置以适配 manualChunks
69
+ adaptSharedToManualChunks(conflictModules)
70
+ }
71
+ })
72
+ }
73
+
74
+ function adaptSharedToManualChunks(conflictModules: Set<string>) {
75
+ // 为冲突模块调整 federation 共享策略
76
+ parsedOptions.prodShared.forEach((sharedItem, index) => {
77
+ const [moduleName, config] = sharedItem as any
78
+
79
+ if (conflictModules.has(moduleName)) {
80
+ // 保持 federation 共享逻辑,但标记为兼容模式
81
+ config.manualChunkCompat = true
82
+ config.chunkLoading = 'defer' // 延迟加载,让 manualChunk 先处理
83
+
84
+ console.info(
85
+ `[Federation] 模块 "${moduleName}" 设置为兼容模式:` +
86
+ `manualChunks 负责分块,federation 负责运行时共享`
87
+ )
88
+ }
89
+ })
90
+ }
91
+
92
+ return {
93
+ name: 'originjs:shared-production',
94
+ virtualFile: {
95
+ __federation_fn_import: federation_fn_import
96
+ },
97
+ options(inputOptions) {
98
+ isRemote = !!parsedOptions.prodExpose.length
99
+ isHost = options.isHost
100
+
101
+ if (shareName2Prop.size) {
102
+ // remove item which is both in external and shared
103
+ inputOptions.external = (
104
+ inputOptions.external as (string | RegExp)[]
105
+ )?.filter((item) => {
106
+ if (item instanceof RegExp)
107
+ return ![...shareName2Prop.keys()].some((key) => item.test(key))
108
+ return !shareName2Prop.has(removeNonRegLetter(item, NAME_CHAR_REG))
109
+ })
110
+ }
111
+
112
+ // 协商共存:处理 manualChunks 与 shared 的兼容
113
+ negotiateManualChunksCompatibility(inputOptions)
114
+
115
+ return inputOptions
116
+ },
117
+ async buildStart() {
118
+ // Cannot emit chunks after module loading has finished, so emitFile first.
119
+ if (parsedOptions.prodShared.length && isRemote) {
120
+ this.emitFile({
121
+ name: '__federation_fn_import',
122
+ type: 'chunk',
123
+ id: '__federation_fn_import',
124
+ preserveSignature: 'strict'
125
+ })
126
+ }
127
+
128
+ // forEach and collect dir
129
+ const collectDirFn = (filePath: string, collect: string[]) => {
130
+ const files = readdirSync(filePath)
131
+ files.forEach((name) => {
132
+ const tempPath = join(filePath, name)
133
+ const isDir = statSync(tempPath).isDirectory()
134
+ if (isDir) {
135
+ collect.push(tempPath)
136
+ collectDirFn(tempPath, collect)
137
+ }
138
+ })
139
+ }
140
+
141
+ const monoRepos: { arr: string[]; root: string | ConfigTypeSet }[] = []
142
+ const dirPaths: string[] = []
143
+ const currentDir = resolve()
144
+ // try to get every module package.json file
145
+ for (const arr of parsedOptions.prodShared) {
146
+ if (isHost && !arr[1].version && !arr[1].manuallyPackagePathSetting) {
147
+ const packageJsonPath = (
148
+ await this.resolve(`${arr[1].packagePath}/package.json`)
149
+ )?.id
150
+ if (packageJsonPath) {
151
+ const packageJson = JSON.parse(
152
+ readFileSync(packageJsonPath, { encoding: 'utf-8' })
153
+ )
154
+ arr[1].version = packageJson.version
155
+ } else {
156
+ arr[1].removed = true
157
+ const dir = join(currentDir, 'node_modules', arr[0])
158
+ const dirStat = statSync(dir)
159
+ if (dirStat.isDirectory()) {
160
+ collectDirFn(dir, dirPaths)
161
+ } else {
162
+ this.error(`cant resolve "${arr[1].packagePath}"`)
163
+ }
164
+
165
+ if (dirPaths.length > 0) {
166
+ monoRepos.push({ arr: dirPaths, root: arr })
167
+ }
168
+ }
169
+
170
+ if (!arr[1].removed && !arr[1].version) {
171
+ this.error(
172
+ `No description file or no version in description file (usually package.json) of ${arr[0]}. Add version to description file, or manually specify version in shared config.`
173
+ )
174
+ }
175
+ }
176
+ }
177
+ parsedOptions.prodShared = parsedOptions.prodShared.filter(
178
+ (item) => !item[1].removed
179
+ )
180
+ // assign version to monoRepo
181
+ if (monoRepos.length > 0) {
182
+ for (const monoRepo of monoRepos) {
183
+ for (const id of monoRepo.arr) {
184
+ try {
185
+ const idResolve = await this.resolve(id)
186
+ if (idResolve?.id) {
187
+ (parsedOptions.prodShared as any[]).push([
188
+ `${monoRepo.root[0]}/${basename(id)}`,
189
+ {
190
+ id: idResolve?.id,
191
+ import: monoRepo.root[1].import,
192
+ shareScope: monoRepo.root[1].shareScope,
193
+ root: monoRepo.root
194
+ }
195
+ ])
196
+ }
197
+ } catch (e) {
198
+ // ignore
199
+ }
200
+ }
201
+ }
202
+ }
203
+
204
+ if (parsedOptions.prodShared.length && isRemote) {
205
+ for (const prod of parsedOptions.prodShared) {
206
+ id2Prop.set(prod[1].id, prod[1])
207
+ }
208
+ }
209
+ },
210
+
211
+ outputOptions: function (outputOption) {
212
+ // remove rollup generated empty imports,like import './filename.js'
213
+ outputOption.hoistTransitiveImports = false
214
+
215
+ const manualChunkFunc = (id: string) => {
216
+ // if id is in shared dependencies, return id ,else return vite function value
217
+ const find = parsedOptions.prodShared.find((arr) =>
218
+ arr[1].dependencies?.has(id)
219
+ )
220
+ return find ? find[0] : undefined
221
+ }
222
+
223
+ // only active when manualChunks is function,array not to solve
224
+ if (typeof outputOption.manualChunks === 'function') {
225
+ outputOption.manualChunks = new Proxy(outputOption.manualChunks, {
226
+ apply(target, thisArg, argArray) {
227
+ const result = manualChunkFunc(argArray[0])
228
+ return result ? result : target(argArray[0], argArray[1])
229
+ }
230
+ })
231
+ }
232
+
233
+ // The default manualChunk function is no longer available from vite 2.9.0
234
+ if (outputOption.manualChunks === undefined) {
235
+ outputOption.manualChunks = manualChunkFunc
236
+ }
237
+
238
+ return outputOption
239
+ },
240
+
241
+ generateBundle(options, bundle) {
242
+ if (!isRemote) {
243
+ return
244
+ }
245
+ const needRemoveShared = new Set<string>()
246
+ for (const key in bundle) {
247
+ const chunk = bundle[key]
248
+ if (chunk.type === 'chunk') {
249
+ if (!isHost) {
250
+ const regRst = sharedFilePathReg.exec(chunk.fileName)
251
+ if (
252
+ regRst &&
253
+ shareName2Prop.get(removeNonRegLetter(regRst[1], NAME_CHAR_REG))
254
+ ?.generate === false
255
+ ) {
256
+ needRemoveShared.add(key)
257
+ }
258
+ }
259
+ }
260
+ }
261
+ if (needRemoveShared.size !== 0) {
262
+ for (const key of needRemoveShared) {
263
+ delete bundle[key]
264
+ }
265
+ }
266
+ }
267
+ }
268
+ }
@@ -0,0 +1,54 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 Origin.js and others.
3
+ //
4
+ // This program and the accompanying materials are licensed under Mulan PSL v2.
5
+ // You can use this software according to the terms and conditions of the Mulan PSL v2.
6
+ // You may obtain a copy of Mulan PSL v2 at:
7
+ // http://license.coscl.org.cn/MulanPSL2
8
+ // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
9
+ // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
10
+ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
11
+ // See the Mulan PSL v2 for more details.
12
+ //
13
+ // SPDX-License-Identifier: MulanPSL-2.0
14
+ // *****************************************************************************
15
+
16
+ import type { ConfigTypeSet, RemotesConfig } from '../types'
17
+ import type { ResolvedConfig } from 'vite'
18
+ import { Remote } from './utils'
19
+ // for generateBundle Hook replace
20
+ export const EXPOSES_MAP = new Map()
21
+ export const EXPOSES_KEY_MAP = new Map()
22
+ export const SHARED = 'shared'
23
+ export const DYNAMIC_LOADING_CSS = 'dynamicLoadingCss'
24
+ export const DYNAMIC_LOADING_CSS_PREFIX = '__v__css__'
25
+ export const DEFAULT_ENTRY_FILENAME = 'remoteEntry.js'
26
+ export const EXTERNALS: string[] = []
27
+ export const ROLLUP = 'rollup'
28
+ export const VITE = 'vite'
29
+ export const VitePluginFederationVersion = '1.5.0'
30
+ export const builderInfo = {
31
+ builder: 'rollup',
32
+ version: '',
33
+ assetsDir: '',
34
+ isHost: false,
35
+ isRemote: false,
36
+ isShared: false,
37
+ }
38
+ export const parsedOptions = {
39
+ prodExpose: [] as (string | ConfigTypeSet)[],
40
+ prodRemote: [] as (string | ConfigTypeSet)[],
41
+ prodShared: [] as (string | ConfigTypeSet)[],
42
+ devShared: [] as (string | ConfigTypeSet)[],
43
+ devExpose: [] as (string | ConfigTypeSet)[],
44
+ devRemote: [] as (string | ConfigTypeSet)[]
45
+ }
46
+ export const devRemotes: {
47
+ id: string
48
+ regexp: RegExp
49
+ config: RemotesConfig
50
+ }[] = []
51
+ export const prodRemotes: Remote[] = []
52
+ export const viteConfigResolved: { config: ResolvedConfig | undefined } = {
53
+ config: undefined
54
+ }
@@ -0,0 +1,247 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 Origin.js and others.
3
+ //
4
+ // This program and the accompanying materials are licensed under Mulan PSL v2.
5
+ // You can use this software according to the terms and conditions of the Mulan PSL v2.
6
+ // You may obtain a copy of Mulan PSL v2 at:
7
+ // http://license.coscl.org.cn/MulanPSL2
8
+ // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
9
+ // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
10
+ // MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
11
+ // See the Mulan PSL v2 for more details.
12
+ //
13
+ // SPDX-License-Identifier: MulanPSL-2.0
14
+ // *****************************************************************************
15
+
16
+ /**
17
+ * Dynamic remote loader for runtime
18
+ * Provides utilities to dynamically load and manage remote components at runtime
19
+ */
20
+
21
+ export interface RemoteOptions {
22
+ url: string | (() => string | Promise<string>);
23
+ format?: 'esm' | 'systemjs' | 'var';
24
+ from?: 'vite' | 'webpack';
25
+ shareScope?: string;
26
+ external?: string | string[];
27
+ externalType?: 'url' | 'promise';
28
+ }
29
+
30
+ export interface RemoteComponentConfig {
31
+ name: string;
32
+ component: string;
33
+ url: string | (() => string | Promise<string>);
34
+ options?: RemoteOptions;
35
+ }
36
+
37
+ /**
38
+ * Dynamic remote component manager
39
+ */
40
+ export class DynamicRemoteManager {
41
+ private remoteCache = new Map<string, any>();
42
+ private loadingPromises = new Map<string, Promise<any>>();
43
+
44
+ /**
45
+ * Add a remote dynamically at runtime
46
+ * @param name Remote name
47
+ * @param config Remote configuration
48
+ */
49
+ async addRemote(name: string, config: RemoteOptions): Promise<void> {
50
+ try {
51
+ // 直接导入虚拟模块,避免使用Function构造器
52
+ // @ts-ignore
53
+ const federationModule = await import('virtual:__federation__');
54
+ const { __federation_method_add_origin_setRemote } = federationModule;
55
+
56
+ // @ts-ignore
57
+ await __federation_method_add_origin_setRemote(name, config.url, {
58
+ format: config.format || 'esm',
59
+ from: config.from || 'vite',
60
+ shareScope: config.shareScope || 'default',
61
+ external: config.external,
62
+ externalType: config.externalType || 'url'
63
+ });
64
+ } catch (error) {
65
+ throw new Error(`Failed to add dynamic remote '${name}': ${error.message}. Make sure to enable dynamic remotes in your plugin configuration with 'enableDynamicRemotes: true'.`);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Load a remote component dynamically
71
+ * @param remoteName Remote name
72
+ * @param componentName Component name to load from remote
73
+ * @returns Promise of the loaded component
74
+ */
75
+ async loadRemoteComponent(remoteName: string, componentName: string): Promise<any> {
76
+ const cacheKey = `${remoteName}/${componentName}`;
77
+
78
+ // Check cache first
79
+ if (this.remoteCache.has(cacheKey)) {
80
+ return this.remoteCache.get(cacheKey);
81
+ }
82
+
83
+ // Check if already loading
84
+ if (this.loadingPromises.has(cacheKey)) {
85
+ return this.loadingPromises.get(cacheKey);
86
+ }
87
+
88
+ // Start loading
89
+ const loadingPromise = this.doLoadRemoteComponent(remoteName, componentName);
90
+ this.loadingPromises.set(cacheKey, loadingPromise);
91
+
92
+ try {
93
+ const component = await loadingPromise;
94
+ this.remoteCache.set(cacheKey, component);
95
+ return component;
96
+ } finally {
97
+ this.loadingPromises.delete(cacheKey);
98
+ }
99
+ }
100
+
101
+ private async doLoadRemoteComponent(remoteName: string, componentName: string): Promise<any> {
102
+ try {
103
+ // 直接导入虚拟模块,避免使用Function构造器
104
+ // @ts-ignore
105
+ const federationModule = await import('virtual:__federation__');
106
+ const { __federation_method_getRemote } = federationModule;
107
+ return __federation_method_getRemote(remoteName, componentName);
108
+ } catch (error) {
109
+ throw new Error(`Failed to load remote component '${remoteName}/${componentName}': ${error.message}. Make sure to enable dynamic remotes in your plugin configuration with 'enableDynamicRemotes: true'.`);
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Preload a remote component
115
+ * @param remoteName Remote name
116
+ * @param componentName Component name
117
+ */
118
+ async preloadRemoteComponent(remoteName: string, componentName: string): Promise<void> {
119
+ try {
120
+ await this.loadRemoteComponent(remoteName, componentName);
121
+ } catch (error) {
122
+ console.warn(`Failed to preload remote component ${remoteName}/${componentName}:`, error);
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Remove a remote component from cache
128
+ * @param remoteName Remote name
129
+ * @param componentName Component name
130
+ */
131
+ clearRemoteComponentCache(remoteName: string, componentName?: string): void {
132
+ if (componentName) {
133
+ const cacheKey = `${remoteName}/${componentName}`;
134
+ this.remoteCache.delete(cacheKey);
135
+ } else {
136
+ // Clear all components for this remote
137
+ for (const key of this.remoteCache.keys()) {
138
+ if (key.startsWith(`${remoteName}/`)) {
139
+ this.remoteCache.delete(key);
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Get all cached remote components
147
+ */
148
+ getCachedRemotes(): string[] {
149
+ return Array.from(this.remoteCache.keys());
150
+ }
151
+
152
+ /**
153
+ * Check if a remote component is cached
154
+ * @param remoteName Remote name
155
+ * @param componentName Component name
156
+ */
157
+ isRemoteComponentCached(remoteName: string, componentName: string): boolean {
158
+ const cacheKey = `${remoteName}/${componentName}`;
159
+ return this.remoteCache.has(cacheKey);
160
+ }
161
+ }
162
+
163
+ // Global instance
164
+ export const dynamicRemoteManager = new DynamicRemoteManager();
165
+
166
+ /**
167
+ * Convenience function to dynamically load remote component
168
+ * @param config Remote component configuration
169
+ */
170
+ export async function loadDynamicRemoteComponent(config: RemoteComponentConfig): Promise<any> {
171
+ // Add remote if not already added
172
+ if (config.options) {
173
+ await dynamicRemoteManager.addRemote(config.name, {
174
+ url: config.url,
175
+ ...config.options
176
+ });
177
+ }
178
+
179
+ // Load the component
180
+ return dynamicRemoteManager.loadRemoteComponent(config.name, config.component);
181
+ }
182
+
183
+ /**
184
+ * Vue 3 composable for dynamic remote loading
185
+ * @param remoteName Remote name
186
+ * @param componentName Component name
187
+ */
188
+ export function useDynamicRemote(remoteName: string, componentName: string) {
189
+ const loading = ref(true);
190
+ // @ts-ignore
191
+ const error = ref<Error | null>(null);
192
+ // @ts-ignore
193
+ const component = ref<any>(null);
194
+
195
+ const loadComponent = async () => {
196
+ try {
197
+ loading.value = true;
198
+ error.value = null;
199
+ component.value = await dynamicRemoteManager.loadRemoteComponent(remoteName, componentName);
200
+ } catch (err) {
201
+ error.value = err instanceof Error ? err : new Error('Failed to load remote component');
202
+ console.error(`Failed to load remote component ${remoteName}/${componentName}:`, err);
203
+ } finally {
204
+ loading.value = false;
205
+ }
206
+ };
207
+
208
+ // Auto-load on mount
209
+ onMounted(() => {
210
+ loadComponent();
211
+ });
212
+
213
+ return {
214
+ component: readonly(component),
215
+ loading: readonly(loading),
216
+ error: readonly(error),
217
+ reload: loadComponent
218
+ };
219
+ }
220
+
221
+ // Helper function to check if we're in a Vue environment
222
+ function isVueAvailable(): boolean {
223
+ try {
224
+ return typeof ref !== 'undefined' && typeof onMounted !== 'undefined';
225
+ } catch {
226
+ return false;
227
+ }
228
+ }
229
+
230
+ // Import Vue composables only if Vue is available
231
+ let ref: any, onMounted: any, readonly: any;
232
+ if (isVueAvailable()) {
233
+ try {
234
+ // 使用动态导入而不是 require
235
+ // @ts-ignore
236
+ import('vue').then(vue => {
237
+ ref = vue.ref;
238
+ onMounted = vue.onMounted;
239
+ readonly = vue.readonly;
240
+ }).catch(() => {
241
+ // Vue not available, composable won't work
242
+ console.warn('Vue not available for dynamic import');
243
+ });
244
+ } catch {
245
+ // Vue not available, composable won't work
246
+ }
247
+ }