@vc-shell/framework 1.0.336 → 1.0.338
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/CHANGELOG.md +14 -0
- package/core/plugins/modularity/README.md +152 -0
- package/core/plugins/modularity/extensions-helper.ts +193 -0
- package/core/plugins/modularity/index.ts +1 -0
- package/core/plugins/modularity/loader.ts +101 -45
- package/dist/core/plugins/modularity/extensions-helper.d.ts +43 -0
- package/dist/core/plugins/modularity/extensions-helper.d.ts.map +1 -0
- package/dist/core/plugins/modularity/index.d.ts +1 -0
- package/dist/core/plugins/modularity/index.d.ts.map +1 -1
- package/dist/core/plugins/modularity/loader.d.ts +8 -1
- package/dist/core/plugins/modularity/loader.d.ts.map +1 -1
- package/dist/framework.js +78 -75
- package/dist/{index-DORisCqC.js → index-2cUYrA-Z.js} +1 -1
- package/dist/{index-B8GFtokC.js → index-AXZgqfdv.js} +16129 -16033
- package/dist/{index-WxuHZ6ax.js → index-BwKmwb9o.js} +1 -1
- package/dist/{index-qjmd8hvu.js → index-CExV_i7E.js} +1 -1
- package/dist/{index-CI2AIc_X.js → index-CIX2TH2D.js} +1 -1
- package/dist/{index-CSSkJOfq.js → index-CRO-H887.js} +1 -1
- package/dist/{index-uN8bAa26.js → index-CSWp3zbD.js} +1 -1
- package/dist/{index-C92JvhP1.js → index-Cr_EBz31.js} +1 -1
- package/dist/{index-BDvESZx7.js → index-D5loliLY.js} +1 -1
- package/dist/{index-BQmhRbEk.js → index-DDakZ6gE.js} +1 -1
- package/dist/{index-CqZkZSbf.js → index-DEuGzebv.js} +1 -1
- package/dist/{index-A-dq3OuG.js → index-DHQKha4a.js} +1 -1
- package/dist/{index-Bgq8S5p_.js → index-DRy2Ks7P.js} +1 -1
- package/dist/{index-lVT4dHp9.js → index-DYpjtoxG.js} +1 -1
- package/dist/{index-CgzWGbHS.js → index-Emn_YJZw.js} +1 -1
- package/dist/{index-Cz8wvRrh.js → index-SiMlQ5U_.js} +1 -1
- package/dist/{index-C7PASwQX.js → index-yNZR-fWu.js} +1 -1
- package/dist/index.css +1 -1
- package/dist/shared/pages/LoginPage/components/login/Login.vue.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/shared/pages/LoginPage/components/login/Login.vue +23 -1
- package/shared/pages/LoginPage/components/login/README.md +52 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [1.0.338](https://github.com/VirtoCommerce/vc-shell/compare/v1.0.337...v1.0.338) (2025-02-05)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **login:** add support for after-form extensions ([6addffd](https://github.com/VirtoCommerce/vc-shell/commit/6addffdc05384cafd360c1f147d227e0691b407c))
|
|
7
|
+
* **modularity:** enhance dynamic module loading and extension support ([309739e](https://github.com/VirtoCommerce/vc-shell/commit/309739eb7aa965530647cc50d4820b5d2e0541f3))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## [1.0.337](https://github.com/VirtoCommerce/vc-shell/compare/v1.0.336...v1.0.337) (2025-01-29)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
1
15
|
## [1.0.336](https://github.com/VirtoCommerce/vc-shell/compare/v1.0.335...v1.0.336) (2025-01-29)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
|
|
2
|
+
# Modularity Plugin
|
|
3
|
+
|
|
4
|
+
The Modularity Plugin provides two main features:
|
|
5
|
+
- Dynamic Module Loading
|
|
6
|
+
- Extensions System
|
|
7
|
+
|
|
8
|
+
## Dynamic Module Loading
|
|
9
|
+
|
|
10
|
+
The Dynamic Module Loading system allows you to load Vue.js modules at runtime.
|
|
11
|
+
|
|
12
|
+
### Usage
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { useDynamicModules } from '@framework/core/plugins/modularity';
|
|
17
|
+
import { createApp } from 'vue';
|
|
18
|
+
import { createRouter } from 'vue-router';
|
|
19
|
+
const app = createApp(App);
|
|
20
|
+
const router = createRouter(/ router config /);
|
|
21
|
+
const { load, extensionsHelper } = useDynamicModules(app, {
|
|
22
|
+
router,
|
|
23
|
+
appName: 'your-app-name'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
baseUrl: 'https://your-modules-host.com/',
|
|
27
|
+
manifestFileName: 'manifest.json', // optional, default: 'manifest.json'
|
|
28
|
+
});
|
|
29
|
+
// Load all modules
|
|
30
|
+
await load();
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Module Structure
|
|
34
|
+
|
|
35
|
+
Each module should have:
|
|
36
|
+
1. A manifest file (manifest.json) describing module files generated by `Vite` during build time
|
|
37
|
+
2. An entry point file that exports a Vue plugin
|
|
38
|
+
3. Optional CSS files
|
|
39
|
+
|
|
40
|
+
Example manifest.json:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"entry": {
|
|
45
|
+
"file": "index.js",
|
|
46
|
+
"isEntry": true
|
|
47
|
+
},
|
|
48
|
+
"styles": {
|
|
49
|
+
"file": "styles.css"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Example module:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
export default {
|
|
58
|
+
install(app, options) {
|
|
59
|
+
// Plugin installation code
|
|
60
|
+
},
|
|
61
|
+
extensions: {
|
|
62
|
+
// Optional extensions configuration
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Extensions System
|
|
68
|
+
|
|
69
|
+
The Extensions System enables communication between the application and its modules.
|
|
70
|
+
|
|
71
|
+
### Types of Extensions
|
|
72
|
+
|
|
73
|
+
1. **Inbound Extensions**: Extensions that allow the application to customize and extend the module's functionality
|
|
74
|
+
2. **Outbound Extensions**: Extensions that the module provides to extend the application's functionality
|
|
75
|
+
|
|
76
|
+
### Registering Extensions
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// In your module
|
|
80
|
+
export default {
|
|
81
|
+
install(app) {
|
|
82
|
+
// Plugin installation code
|
|
83
|
+
},
|
|
84
|
+
extensions: {
|
|
85
|
+
inbound: {
|
|
86
|
+
'customization-point': {
|
|
87
|
+
// Extensions that this module accepts
|
|
88
|
+
someConfig: true
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
outbound: {
|
|
92
|
+
'extension-point': [
|
|
93
|
+
// Extensions this module provides
|
|
94
|
+
{
|
|
95
|
+
id: 'MyExtension',
|
|
96
|
+
component: MyComponent,
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Using Extensions Helper
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { useExtensionsHelper } from '@vc-shell/framework';
|
|
108
|
+
import { inject } from 'vue';
|
|
109
|
+
|
|
110
|
+
// In your component setup
|
|
111
|
+
const extensionsHelper = inject(extensionsHelperSymbol);
|
|
112
|
+
// Get extensions provided by other modules
|
|
113
|
+
const outboundExtensions = extensionsHelper.getOutboundExtensions('extension-point');
|
|
114
|
+
// Get extensions configured for your module
|
|
115
|
+
const inboundExtensions = extensionsHelper.getInboundExtensions('your-module-id', 'customization-point');
|
|
116
|
+
// Get all extensions for a module
|
|
117
|
+
const moduleExtensions = extensionsHelper.getModuleExtensions('your-module-id');
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Extension Types
|
|
121
|
+
|
|
122
|
+
1. **Component Extensions**
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
interface ExtensionPoint = {
|
|
126
|
+
id: string;
|
|
127
|
+
component: Component;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
2. **Composable Function Extensions**
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
interface ComposableFunction = {
|
|
135
|
+
id: string;
|
|
136
|
+
fn: (...args: unknown[]) => unknown;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
3. **Configuration Object Extensions**
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
type Extension = Record<string, unknown>;
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Best Practices
|
|
147
|
+
|
|
148
|
+
1. Always provide unique IDs for extensions
|
|
149
|
+
2. Document your module's extension points
|
|
150
|
+
3. Handle missing or failed extensions gracefully
|
|
151
|
+
4. Use TypeScript interfaces for type safety
|
|
152
|
+
5. Keep extension configurations simple and serializable
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { App, computed, ComputedRef, InjectionKey } from "vue";
|
|
2
|
+
|
|
3
|
+
declare module "@vue/runtime-core" {
|
|
4
|
+
interface ComponentCustomProperties {
|
|
5
|
+
$extensions: ExtensionRegistry;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ExtensionPoint extends Extension {
|
|
10
|
+
id: string;
|
|
11
|
+
component: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ComposableFunction {
|
|
15
|
+
id: string;
|
|
16
|
+
fn: (...args: unknown[]) => unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type Extension = Record<string, unknown>;
|
|
20
|
+
|
|
21
|
+
export interface ExtensionRegistry {
|
|
22
|
+
inbound: {
|
|
23
|
+
[namespace: string]: {
|
|
24
|
+
[extensionPoint: string]: Extension;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
outbound: {
|
|
28
|
+
[namespace: string]: {
|
|
29
|
+
[extensionPoint: string]: ExtensionPoint[] | ComposableFunction[] | Extension;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ExtensionNamespace {
|
|
35
|
+
[point: string]: ExtensionPoint[] | ComposableFunction[] | Extension;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ExtensionsHelper {
|
|
39
|
+
getInboundExtensions(namespace: string, point?: string): Extension;
|
|
40
|
+
getOutboundExtensions(point: string): (ExtensionPoint[] | ComposableFunction[] | Extension)[];
|
|
41
|
+
getModuleExtensions(namespace: string): {
|
|
42
|
+
inbound: Record<string, Extension>;
|
|
43
|
+
outbound: Record<string, ExtensionPoint[] | ComposableFunction[] | Extension>;
|
|
44
|
+
};
|
|
45
|
+
extensions: ComputedRef<ExtensionRegistry>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const extensionsHelperSymbol = Symbol("extensionsHelper") as InjectionKey<ExtensionsHelper>;
|
|
49
|
+
|
|
50
|
+
export function createExtensionsHelper(app: App): ExtensionsHelper {
|
|
51
|
+
if (!app.config.globalProperties.$extensions) {
|
|
52
|
+
app.config.globalProperties.$extensions = {
|
|
53
|
+
inbound: {},
|
|
54
|
+
outbound: {},
|
|
55
|
+
} as ExtensionRegistry;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const helper: ExtensionsHelper = {
|
|
59
|
+
getInboundExtensions(namespace: string, point?: string) {
|
|
60
|
+
return point
|
|
61
|
+
? app.config.globalProperties.$extensions.inbound[namespace][point]
|
|
62
|
+
: app.config.globalProperties.$extensions.inbound[namespace];
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
getOutboundExtensions(point: string) {
|
|
66
|
+
const result: (ExtensionPoint[] | ComposableFunction[] | Extension)[] = [];
|
|
67
|
+
Object.values(app.config.globalProperties.$extensions.outbound).forEach((namespace: unknown) => {
|
|
68
|
+
const typedNamespace = namespace as ExtensionNamespace;
|
|
69
|
+
if (typedNamespace[point]) {
|
|
70
|
+
if (Array.isArray(typedNamespace[point])) {
|
|
71
|
+
result.push(...(typedNamespace[point] as ExtensionPoint[]));
|
|
72
|
+
} else if (typeof typedNamespace[point] === "object" && "fn" in typedNamespace[point]) {
|
|
73
|
+
result.push(typedNamespace[point]);
|
|
74
|
+
} else {
|
|
75
|
+
result.push(typedNamespace[point]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return result;
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
getModuleExtensions(namespace: string) {
|
|
84
|
+
return {
|
|
85
|
+
inbound: app.config.globalProperties.$extensions.inbound[namespace] || {},
|
|
86
|
+
outbound: app.config.globalProperties.$extensions.outbound[namespace] || {},
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
extensions: computed(() => app.config.globalProperties.$extensions),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// Provide access to extensions through provide/inject
|
|
94
|
+
app.provide(extensionsHelperSymbol, helper);
|
|
95
|
+
|
|
96
|
+
// Provide direct access to extensions
|
|
97
|
+
app.provide("$extensions", app.config.globalProperties.$extensions);
|
|
98
|
+
|
|
99
|
+
return helper;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function registerModuleExtensions(app: App, moduleId: string, extensions: ExtensionRegistry) {
|
|
103
|
+
if (!app.config.globalProperties.$extensions) {
|
|
104
|
+
app.config.globalProperties.$extensions = {
|
|
105
|
+
inbound: {},
|
|
106
|
+
outbound: {},
|
|
107
|
+
} as ExtensionRegistry;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const { inbound, outbound } = app.config.globalProperties.$extensions;
|
|
111
|
+
|
|
112
|
+
// Register inbound extensions
|
|
113
|
+
registerInboundExtensions(inbound, moduleId, extensions);
|
|
114
|
+
|
|
115
|
+
// Register outbound extensions
|
|
116
|
+
registerOutboundExtensions(outbound, moduleId, extensions);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function registerInboundExtensions(
|
|
120
|
+
inbound: ExtensionRegistry["inbound"],
|
|
121
|
+
moduleId: string,
|
|
122
|
+
extensions: ExtensionRegistry,
|
|
123
|
+
) {
|
|
124
|
+
if (!inbound[moduleId]) {
|
|
125
|
+
inbound[moduleId] = {};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
Object.entries(extensions.inbound || {}).forEach(([point, extension]) => {
|
|
129
|
+
if (typeof extension !== "object" || Array.isArray(extension)) {
|
|
130
|
+
console.warn(
|
|
131
|
+
`Invalid inbound extension type for point "${point}" in module "${moduleId}". Inbound extensions must be objects.`,
|
|
132
|
+
);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
inbound[moduleId][point] = {
|
|
137
|
+
...(inbound[moduleId][point] || {}),
|
|
138
|
+
...extension,
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function registerOutboundExtensions(
|
|
144
|
+
outbound: ExtensionRegistry["outbound"],
|
|
145
|
+
moduleId: string,
|
|
146
|
+
extensions: ExtensionRegistry,
|
|
147
|
+
) {
|
|
148
|
+
if (!outbound[moduleId]) {
|
|
149
|
+
outbound[moduleId] = {};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
Object.entries(extensions.outbound || {}).forEach(([point, extension]) => {
|
|
153
|
+
initializeExtensionPoint(outbound[moduleId], point, extension);
|
|
154
|
+
mergeExtension(outbound[moduleId], point, extension);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function initializeExtensionPoint(
|
|
159
|
+
moduleOutbound: ExtensionRegistry["outbound"][string],
|
|
160
|
+
point: string,
|
|
161
|
+
extension: ExtensionPoint[] | ComposableFunction | Extension,
|
|
162
|
+
) {
|
|
163
|
+
if (!moduleOutbound[point]) {
|
|
164
|
+
if (Array.isArray(extension)) {
|
|
165
|
+
moduleOutbound[point] = [];
|
|
166
|
+
} else if (typeof extension === "object" && "fn" in extension) {
|
|
167
|
+
moduleOutbound[point] = [];
|
|
168
|
+
} else if (typeof extension === "object") {
|
|
169
|
+
moduleOutbound[point] = {};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function mergeExtension(
|
|
175
|
+
moduleOutbound: ExtensionRegistry["outbound"][string],
|
|
176
|
+
point: string,
|
|
177
|
+
extension: ExtensionPoint[] | ComposableFunction | Extension,
|
|
178
|
+
) {
|
|
179
|
+
if (Array.isArray(extension) && Array.isArray(moduleOutbound[point])) {
|
|
180
|
+
(moduleOutbound[point] as ExtensionPoint[]).push(...(extension as ExtensionPoint[]));
|
|
181
|
+
} else if (typeof extension === "object" && "fn" in extension && Array.isArray(moduleOutbound[point])) {
|
|
182
|
+
(moduleOutbound[point] as ComposableFunction[]).push(extension as ComposableFunction);
|
|
183
|
+
} else if (isObjectExtension(extension) && isObjectExtension(moduleOutbound[point])) {
|
|
184
|
+
moduleOutbound[point] = {
|
|
185
|
+
...(moduleOutbound[point] as Extension),
|
|
186
|
+
...extension,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function isObjectExtension(value: unknown): value is Extension {
|
|
192
|
+
return typeof value === "object" && !Array.isArray(value);
|
|
193
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { App, Plugin } from "vue";
|
|
2
2
|
import { Router } from "vue-router";
|
|
3
|
+
import { ExtensionRegistry, createExtensionsHelper, registerModuleExtensions } from "./extensions-helper";
|
|
3
4
|
|
|
4
5
|
interface ModuleManifest {
|
|
5
6
|
file: string;
|
|
@@ -23,21 +24,54 @@ interface Apps {
|
|
|
23
24
|
};
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
interface ModuleWithDefaultExport {
|
|
28
|
+
default: {
|
|
29
|
+
install: Plugin;
|
|
30
|
+
extensions?: ExtensionRegistry;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface ModuleWithNamedExport {
|
|
35
|
+
install: Plugin;
|
|
36
|
+
extensions?: ExtensionRegistry;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type ModuleExports = ModuleWithDefaultExport | ModuleWithNamedExport;
|
|
40
|
+
|
|
41
|
+
function loadCSS(url: string): Promise<void> {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
28
43
|
const link = document.createElement("link");
|
|
29
44
|
link.rel = "stylesheet";
|
|
30
45
|
link.href = url;
|
|
31
46
|
link.onload = () => resolve();
|
|
32
|
-
link.onerror = () => reject(new Error(`Failed to load CSS: ${url}`));
|
|
47
|
+
link.onerror = (e) => reject(new Error(`Failed to load CSS: ${url}, error: ${e}`));
|
|
33
48
|
document.head.appendChild(link);
|
|
34
49
|
});
|
|
35
50
|
}
|
|
36
51
|
|
|
37
|
-
|
|
52
|
+
interface ModuleConfig {
|
|
53
|
+
baseUrl: string;
|
|
54
|
+
manifestFileName: string;
|
|
55
|
+
entryPointKey: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const DEFAULT_CONFIG: Partial<ModuleConfig> = {
|
|
59
|
+
manifestFileName: "manifest.json",
|
|
60
|
+
entryPointKey: "isEntry",
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export function useDynamicModules(
|
|
64
|
+
app: App,
|
|
65
|
+
{ router, appName }: { router: Router; appName: string },
|
|
66
|
+
config: Partial<ModuleConfig> = {},
|
|
67
|
+
) {
|
|
68
|
+
const finalConfig = { ...DEFAULT_CONFIG, ...config };
|
|
69
|
+
const loadedModules = new Set<string>();
|
|
70
|
+
const extensionsHelper = createExtensionsHelper(app);
|
|
71
|
+
|
|
38
72
|
async function load() {
|
|
39
73
|
try {
|
|
40
|
-
const appsUrl = "
|
|
74
|
+
const appsUrl = finalConfig.baseUrl + "apps.json";
|
|
41
75
|
const modules: Apps[] = await fetch(appsUrl).then((res) => res.json());
|
|
42
76
|
|
|
43
77
|
const module = modules.find((module) => module[appName]);
|
|
@@ -48,68 +82,90 @@ export function useDynamicModules(app: App, { router, appName }: { router: Route
|
|
|
48
82
|
if (!(appModules && appModules.length)) {
|
|
49
83
|
throw new Error("Modules not found");
|
|
50
84
|
}
|
|
85
|
+
const manifestPromises = appModules.map(async (module) => {
|
|
86
|
+
if (loadedModules.has(module.id)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
51
89
|
|
|
52
|
-
|
|
90
|
+
const moduleUrl = module.url.replace(/^([^/])/, "/$1").replace(/([^/])$/, "$1/");
|
|
53
91
|
try {
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (!manifestResponse.ok) {
|
|
58
|
-
console.error(`Failed to load manifest for module ${module.id}:`, manifestResponse.statusText);
|
|
59
|
-
continue;
|
|
92
|
+
const response = await fetch(moduleUrl + finalConfig.manifestFileName);
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
throw new Error(`Failed to load manifest for module '{ ${module.id} }': ${response.statusText}`);
|
|
60
95
|
}
|
|
96
|
+
return {
|
|
97
|
+
moduleId: module.id,
|
|
98
|
+
moduleUrl,
|
|
99
|
+
manifest: (await response.json()) as Manifest,
|
|
100
|
+
};
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error(error);
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
61
106
|
|
|
62
|
-
|
|
107
|
+
const manifestResults = await Promise.all(manifestPromises);
|
|
108
|
+
const validManifests = manifestResults.filter(
|
|
109
|
+
(result): result is NonNullable<typeof result> => result !== null,
|
|
110
|
+
);
|
|
63
111
|
|
|
64
|
-
|
|
112
|
+
for (const { moduleId, moduleUrl, manifest } of validManifests) {
|
|
113
|
+
try {
|
|
65
114
|
const entry = Object.values(manifest).find((file) => (file as ModuleManifest).isEntry);
|
|
66
|
-
|
|
67
115
|
if (!entry) {
|
|
68
|
-
|
|
69
|
-
continue;
|
|
116
|
+
throw new Error(`Entry file not found for module ${moduleId}`);
|
|
70
117
|
}
|
|
71
118
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
.filter((file) => file.file.endsWith(".css"))
|
|
76
|
-
.map((file) => loadCSS(moduleUrl + `${file.file}`)),
|
|
77
|
-
).catch((error) => {
|
|
78
|
-
console.error(`Failed to load styles for module ${module.id}:`, error);
|
|
79
|
-
});
|
|
119
|
+
const cssFiles = Object.values(manifest)
|
|
120
|
+
.filter((file) => file.file.endsWith(".css"))
|
|
121
|
+
.map((file) => loadCSS(moduleUrl + file.file));
|
|
80
122
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
console.error(`Failed to load module ${module.id}:`, error);
|
|
84
|
-
return; // Skip to next module
|
|
123
|
+
await Promise.all(cssFiles).catch((error) => {
|
|
124
|
+
console.error(`Failed to load styles for module ${moduleId}:`, error);
|
|
85
125
|
});
|
|
86
126
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
127
|
+
await import(/* @vite-ignore */ moduleUrl + entry.file);
|
|
128
|
+
|
|
129
|
+
if (typeof window !== "undefined" && window.VcShellDynamicModules) {
|
|
130
|
+
Object.values(window.VcShellDynamicModules).forEach((mod) => {
|
|
131
|
+
try {
|
|
132
|
+
const moduleExports = mod as ModuleExports;
|
|
133
|
+
const moduleToInstall = "default" in moduleExports ? moduleExports.default : moduleExports;
|
|
134
|
+
|
|
135
|
+
if ("install" in moduleToInstall) {
|
|
136
|
+
app.use(moduleToInstall.install as Plugin, { router });
|
|
137
|
+
|
|
138
|
+
if (moduleToInstall.extensions) {
|
|
139
|
+
registerModuleExtensions(app, moduleId, moduleToInstall.extensions);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
loadedModules.add(moduleId);
|
|
143
|
+
} else {
|
|
144
|
+
console.error(`Module ${moduleId} does not have an 'install' function`);
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error(`Failed to register plugin for module ${moduleId}:`, error);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
101
151
|
} catch (error) {
|
|
102
|
-
console.error(`Failed to process module ${
|
|
103
|
-
continue; // Skip to next module
|
|
152
|
+
console.error(`Failed to process module ${moduleId}:`, error);
|
|
104
153
|
}
|
|
105
154
|
}
|
|
106
155
|
}
|
|
107
156
|
} catch (error) {
|
|
108
157
|
console.error("Failed to load modules:", error);
|
|
109
158
|
}
|
|
159
|
+
|
|
160
|
+
app.config.globalProperties.$dynamicModules = {
|
|
161
|
+
...(app.config.globalProperties.$dynamicModules || {}),
|
|
162
|
+
...window.VcShellDynamicModules,
|
|
163
|
+
};
|
|
164
|
+
app.provide("$dynamicModules", app.config.globalProperties.$dynamicModules);
|
|
110
165
|
}
|
|
111
166
|
|
|
112
167
|
return {
|
|
113
168
|
load,
|
|
169
|
+
extensionsHelper,
|
|
114
170
|
};
|
|
115
171
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { App, ComputedRef, InjectionKey } from "vue";
|
|
2
|
+
declare module "@vue/runtime-core" {
|
|
3
|
+
interface ComponentCustomProperties {
|
|
4
|
+
$extensions: ExtensionRegistry;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export interface ExtensionPoint extends Extension {
|
|
8
|
+
id: string;
|
|
9
|
+
component: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface ComposableFunction {
|
|
12
|
+
id: string;
|
|
13
|
+
fn: (...args: unknown[]) => unknown;
|
|
14
|
+
}
|
|
15
|
+
export type Extension = Record<string, unknown>;
|
|
16
|
+
export interface ExtensionRegistry {
|
|
17
|
+
inbound: {
|
|
18
|
+
[namespace: string]: {
|
|
19
|
+
[extensionPoint: string]: Extension;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
outbound: {
|
|
23
|
+
[namespace: string]: {
|
|
24
|
+
[extensionPoint: string]: ExtensionPoint[] | ComposableFunction[] | Extension;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface ExtensionNamespace {
|
|
29
|
+
[point: string]: ExtensionPoint[] | ComposableFunction[] | Extension;
|
|
30
|
+
}
|
|
31
|
+
export interface ExtensionsHelper {
|
|
32
|
+
getInboundExtensions(namespace: string, point?: string): Extension;
|
|
33
|
+
getOutboundExtensions(point: string): (ExtensionPoint[] | ComposableFunction[] | Extension)[];
|
|
34
|
+
getModuleExtensions(namespace: string): {
|
|
35
|
+
inbound: Record<string, Extension>;
|
|
36
|
+
outbound: Record<string, ExtensionPoint[] | ComposableFunction[] | Extension>;
|
|
37
|
+
};
|
|
38
|
+
extensions: ComputedRef<ExtensionRegistry>;
|
|
39
|
+
}
|
|
40
|
+
export declare const extensionsHelperSymbol: InjectionKey<ExtensionsHelper>;
|
|
41
|
+
export declare function createExtensionsHelper(app: App): ExtensionsHelper;
|
|
42
|
+
export declare function registerModuleExtensions(app: App, moduleId: string, extensions: ExtensionRegistry): void;
|
|
43
|
+
//# sourceMappingURL=extensions-helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensions-helper.d.ts","sourceRoot":"","sources":["../../../../core/plugins/modularity/extensions-helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAY,WAAW,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AAE/D,OAAO,QAAQ,mBAAmB,CAAC;IACjC,UAAU,yBAAyB;QACjC,WAAW,EAAE,iBAAiB,CAAC;KAChC;CACF;AAED,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;CACrC;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE;QACP,CAAC,SAAS,EAAE,MAAM,GAAG;YACnB,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;SACrC,CAAC;KACH,CAAC;IACF,QAAQ,EAAE;QACR,CAAC,SAAS,EAAE,MAAM,GAAG;YACnB,CAAC,cAAc,EAAE,MAAM,GAAG,cAAc,EAAE,GAAG,kBAAkB,EAAE,GAAG,SAAS,CAAC;SAC/E,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE,GAAG,kBAAkB,EAAE,GAAG,SAAS,CAAC;CACtE;AAED,MAAM,WAAW,gBAAgB;IAC/B,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnE,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,cAAc,EAAE,GAAG,kBAAkB,EAAE,GAAG,SAAS,CAAC,EAAE,CAAC;IAC9F,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG;QACtC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACnC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,kBAAkB,EAAE,GAAG,SAAS,CAAC,CAAC;KAC/E,CAAC;IACF,UAAU,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAC;CAC5C;AAED,eAAO,MAAM,sBAAsB,gCAA+D,CAAC;AAEnG,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,GAAG,GAAG,gBAAgB,CAkDjE;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,QAejG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../core/plugins/modularity/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAY,MAAM,KAAK,CAAC;AAE/C,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,wBAAwB,EAAc,MAAM,qDAAqD,CAAC;AAM3G,eAAO,MAAM,YAAY;;aAAuE,OAAO;iBACxF,GAAG,GAAG,IAAI;CAsBvB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;;;iBAOX,GAAG,YAAY;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;CA8MxD,CAAC;AAEF,cAAc,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../core/plugins/modularity/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAY,MAAM,KAAK,CAAC;AAE/C,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,wBAAwB,EAAc,MAAM,qDAAqD,CAAC;AAM3G,eAAO,MAAM,YAAY;;aAAuE,OAAO;iBACxF,GAAG,GAAG,IAAI;CAsBvB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;;;iBAOX,GAAG,YAAY;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;CA8MxD,CAAC;AAEF,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC"}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { App } from "vue";
|
|
2
2
|
import { Router } from "vue-router";
|
|
3
|
+
interface ModuleConfig {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
manifestFileName: string;
|
|
6
|
+
entryPointKey: string;
|
|
7
|
+
}
|
|
3
8
|
export declare function useDynamicModules(app: App, { router, appName }: {
|
|
4
9
|
router: Router;
|
|
5
10
|
appName: string;
|
|
6
|
-
}): {
|
|
11
|
+
}, config?: Partial<ModuleConfig>): {
|
|
7
12
|
load: () => Promise<void>;
|
|
13
|
+
extensionsHelper: import("./extensions-helper").ExtensionsHelper;
|
|
8
14
|
};
|
|
15
|
+
export {};
|
|
9
16
|
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../../core/plugins/modularity/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAU,MAAM,KAAK,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../../core/plugins/modularity/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAU,MAAM,KAAK,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAkDpC,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;CACvB;AAOD,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,GAAG,EACR,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACxD,MAAM,GAAE,OAAO,CAAC,YAAY,CAAM;;;EAyGnC"}
|