@kaena1/mm-ui-shared-utils 1.25.0-f-poc-EC-3301.1 → 1.25.0-f-poc-EC-3301.3
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/dist/_virtual/_commonjsHelpers.js +6 -0
- package/dist/_virtual/_interop_require_default.js +4 -0
- package/dist/_virtual/_interop_require_wildcard.js +4 -0
- package/dist/_virtual/head-manager-context.shared-runtime.js +4 -0
- package/dist/_virtual/head-manager.js +4 -0
- package/dist/_virtual/request-idle-callback.js +4 -0
- package/dist/_virtual/script.js +4 -0
- package/dist/components/ImportMapScripts.js +36 -0
- package/dist/components/RemoteLinks.js +10 -0
- package/dist/components/RemoteRenderer.js +27 -0
- package/dist/hooks/useRemoteComponent.js +69 -0
- package/dist/index.d.ts +50 -7
- package/dist/index.js +20 -297
- package/dist/node_modules/@swc/helpers/cjs/_interop_require_default.js +8 -0
- package/dist/node_modules/@swc/helpers/cjs/_interop_require_wildcard.js +31 -0
- package/dist/node_modules/next/dist/client/head-manager.js +206 -0
- package/dist/node_modules/next/dist/client/request-idle-callback.js +48 -0
- package/dist/node_modules/next/dist/client/script.js +298 -0
- package/dist/node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.js +30 -0
- package/dist/node_modules/next/script.js +7 -0
- package/dist/packages/shared-utils/package.json.js +11 -0
- package/dist/services/ImportMapScript.js +26 -0
- package/dist/services/ImportMapService.js +61 -0
- package/dist/services/fetchManifest.js +31 -0
- package/dist/services/moduleLoader.js +155 -0
- package/package.json +6 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
async function fetchManifest(manifestUrl) {
|
|
2
|
+
try {
|
|
3
|
+
const response = await fetch(manifestUrl, {
|
|
4
|
+
headers: {
|
|
5
|
+
"Content-Type": "application/json"
|
|
6
|
+
}
|
|
7
|
+
});
|
|
8
|
+
if (!response.ok) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
`Failed to fetch manifest: ${response.status} ${response.statusText}`
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
const data = await response.json();
|
|
14
|
+
if (!data || typeof data !== "object" || !data.components || typeof data.components !== "object") {
|
|
15
|
+
throw new Error("Invalid manifest: missing 'components' object");
|
|
16
|
+
}
|
|
17
|
+
return data;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (error instanceof Error) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`Manifest loading failed from ${manifestUrl}: ${error.message}`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Manifest loading failed from ${manifestUrl}: Unknown error`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
fetchManifest
|
|
31
|
+
};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import React from "react";
|
|
5
|
+
import ReactDOM from "react-dom";
|
|
6
|
+
class ModuleLoader {
|
|
7
|
+
constructor() {
|
|
8
|
+
__publicField(this, "loadedModules", /* @__PURE__ */ new Map());
|
|
9
|
+
__publicField(this, "importMapInjected", false);
|
|
10
|
+
}
|
|
11
|
+
prepareGlobalDependencies() {
|
|
12
|
+
if (!window.React) {
|
|
13
|
+
window.React = React;
|
|
14
|
+
}
|
|
15
|
+
if (!window.ReactDOM) {
|
|
16
|
+
window.ReactDOM = ReactDOM;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Ensure import map is injected before loading any modules
|
|
21
|
+
* Import maps MUST be added before any module scripts
|
|
22
|
+
*/
|
|
23
|
+
ensureImportMap() {
|
|
24
|
+
if (typeof window === "undefined") return;
|
|
25
|
+
if (this.importMapInjected) return;
|
|
26
|
+
const existingImportMap = document.querySelector(
|
|
27
|
+
'script[type="importmap"], script[type="importmap-shim"]'
|
|
28
|
+
);
|
|
29
|
+
if (existingImportMap) {
|
|
30
|
+
this.importMapInjected = true;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
console.warn(
|
|
34
|
+
`Import map not found. Ensure an import map is included early in:
|
|
35
|
+
|
|
36
|
+
- pages router: the document head (e.g. in pages/_document.tsx).
|
|
37
|
+
|
|
38
|
+
- app router: the root layout head (e.g. in app/layout.tsx).`
|
|
39
|
+
);
|
|
40
|
+
this.importMapInjected = true;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Load remote React component from ES module URL
|
|
44
|
+
*
|
|
45
|
+
* @param options - Configuration object with bundleUrl and componentName
|
|
46
|
+
* @returns Promise that resolves to the loaded React component
|
|
47
|
+
* @throws Error if component loading fails or times out (10s)
|
|
48
|
+
*/
|
|
49
|
+
async loadComponent(options) {
|
|
50
|
+
const { bundleUrl, componentName } = options;
|
|
51
|
+
if (this.loadedModules.has(bundleUrl)) {
|
|
52
|
+
const cachedModule = this.loadedModules.get(bundleUrl);
|
|
53
|
+
const component = (cachedModule == null ? void 0 : cachedModule[componentName]) || (cachedModule == null ? void 0 : cachedModule.default);
|
|
54
|
+
if (component) return component;
|
|
55
|
+
}
|
|
56
|
+
this.prepareGlobalDependencies();
|
|
57
|
+
this.ensureImportMap();
|
|
58
|
+
try {
|
|
59
|
+
const esm = await this.loadModuleViaScript(bundleUrl);
|
|
60
|
+
const component = esm[componentName] || esm.default;
|
|
61
|
+
if (!component) {
|
|
62
|
+
throw new Error(`Component ${componentName} not found in module`);
|
|
63
|
+
}
|
|
64
|
+
this.loadedModules.set(bundleUrl, esm);
|
|
65
|
+
return component;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error("🔍 LOADER: Failed to load component:", error);
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Load ES module from external URL using dynamic script injection
|
|
73
|
+
* This bypasses Webpack's module resolution and works with import maps
|
|
74
|
+
*/
|
|
75
|
+
loadModuleViaScript(url) {
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
const callbackName = `__moduleCallback_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
78
|
+
const checkInterval = 50;
|
|
79
|
+
const maxAttempts = 200;
|
|
80
|
+
let attempts = 0;
|
|
81
|
+
const scriptContent = `
|
|
82
|
+
import * as loadedModule from '${url}';
|
|
83
|
+
window.${callbackName} = loadedModule;
|
|
84
|
+
`;
|
|
85
|
+
const script = document.createElement("script");
|
|
86
|
+
script.type = "module-shim";
|
|
87
|
+
script.textContent = scriptContent;
|
|
88
|
+
const pollForModule = setInterval(() => {
|
|
89
|
+
attempts++;
|
|
90
|
+
if (window[callbackName]) {
|
|
91
|
+
clearInterval(pollForModule);
|
|
92
|
+
const loadedModule = window[callbackName];
|
|
93
|
+
delete window[callbackName];
|
|
94
|
+
if (script.parentNode) {
|
|
95
|
+
document.head.removeChild(script);
|
|
96
|
+
}
|
|
97
|
+
resolve(loadedModule);
|
|
98
|
+
} else if (attempts >= maxAttempts) {
|
|
99
|
+
clearInterval(pollForModule);
|
|
100
|
+
delete window[callbackName];
|
|
101
|
+
if (script.parentNode) {
|
|
102
|
+
document.head.removeChild(script);
|
|
103
|
+
}
|
|
104
|
+
reject(new Error(`Timeout loading module from ${url}`));
|
|
105
|
+
}
|
|
106
|
+
}, checkInterval);
|
|
107
|
+
script.onerror = (_err) => {
|
|
108
|
+
clearInterval(pollForModule);
|
|
109
|
+
delete window[callbackName];
|
|
110
|
+
if (script.parentNode) {
|
|
111
|
+
document.head.removeChild(script);
|
|
112
|
+
}
|
|
113
|
+
reject(new Error(`Failed to load module from ${url}`));
|
|
114
|
+
};
|
|
115
|
+
document.head.appendChild(script);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Preload a module without extracting components
|
|
120
|
+
* Useful for warming up cache
|
|
121
|
+
*
|
|
122
|
+
* @param bundleUrl - URL of the module to preload
|
|
123
|
+
*/
|
|
124
|
+
async preloadModule(bundleUrl) {
|
|
125
|
+
if (this.loadedModules.has(bundleUrl)) return;
|
|
126
|
+
this.ensureImportMap();
|
|
127
|
+
try {
|
|
128
|
+
const esm = await this.loadModuleViaScript(bundleUrl);
|
|
129
|
+
this.loadedModules.set(bundleUrl, esm);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error(`Failed to preload module ${bundleUrl}:`, error);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Check if a module has already been loaded
|
|
136
|
+
*
|
|
137
|
+
* @param bundleUrl - URL of the module to check
|
|
138
|
+
* @returns true if module is already loaded, false otherwise
|
|
139
|
+
*/
|
|
140
|
+
isLoaded(bundleUrl) {
|
|
141
|
+
return this.loadedModules.has(bundleUrl);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Clear all loaded modules from cache
|
|
145
|
+
* Useful for development/testing
|
|
146
|
+
*/
|
|
147
|
+
clearCache() {
|
|
148
|
+
this.loadedModules.clear();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const moduleLoader = new ModuleLoader();
|
|
152
|
+
export {
|
|
153
|
+
ModuleLoader,
|
|
154
|
+
moduleLoader
|
|
155
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaena1/mm-ui-shared-utils",
|
|
3
|
-
"version": "1.25.0-f-poc-EC-3301.
|
|
3
|
+
"version": "1.25.0-f-poc-EC-3301.3",
|
|
4
4
|
"description": "Shared utilities for loading remote React components in Mint Mobile microfrontends",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
],
|
|
36
36
|
"author": "",
|
|
37
37
|
"license": "ISC",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"react": "^18.3.1",
|
|
40
|
+
"react-dom": "^18.3.1"
|
|
41
|
+
},
|
|
38
42
|
"peerDependencies": {
|
|
39
43
|
"react": "^18.3.1",
|
|
40
44
|
"react-dom": "^18.3.1"
|
|
@@ -54,6 +58,7 @@
|
|
|
54
58
|
"eslint": "^9.39.2",
|
|
55
59
|
"react": "^18.3.1",
|
|
56
60
|
"react-dom": "^18.3.1",
|
|
61
|
+
"read-pkg-up": "^11.0.0",
|
|
57
62
|
"rollup-preserve-directives": "^1.1.3",
|
|
58
63
|
"semantic-release": "^23.0.0",
|
|
59
64
|
"semantic-release-slack-bot": "^4.0.2",
|