@asteby/metacore-runtime-react 13.0.0 → 13.1.0
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 +8 -0
- package/dist/addon-loader.d.ts.map +1 -1
- package/dist/addon-loader.js +49 -10
- package/package.json +1 -1
- package/src/addon-loader.tsx +55 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 13.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8d9c602: AddonLoader carga remotes de federación ESM vía `import()` dinámico (fix "Cannot use import statement outside a module").
|
|
8
|
+
|
|
9
|
+
Los remotes built con Vite/@originjs `format:"esm"` (el estándar de `metacoreFederationShared`) son módulos ES que hacen `import` top-level y exportan `{ init, get }` — DEBEN cargarse como módulo. El `AddonLoader` los inyectaba como `<script>` clásico → el browser tiraba `Cannot use import statement outside a module` y la UI federada nunca cargaba. Ahora hace `import()` dinámico (vía `new Function` para que ningún bundler reescriba el import del URL externo) y usa el namespace del módulo como container; los remotes legacy "var"/window siguen soportados con fallback a `<script>` + `window[scope]`.
|
|
10
|
+
|
|
3
11
|
## 13.0.0
|
|
4
12
|
|
|
5
13
|
### Minor Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"addon-loader.d.ts","sourceRoot":"","sources":["../src/addon-loader.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAGjE,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;QAClB,wBAAwB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;QAC3D,wBAAwB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACrD;CACJ;AAED,MAAM,WAAW,gBAAgB;IAC7B,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAA;IACb,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAA;IACX,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+DAA+D;IAC/D,GAAG,EAAE,QAAQ,CAAA;IACb,wCAAwC;IACxC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;IAC9B;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC7B;
|
|
1
|
+
{"version":3,"file":"addon-loader.d.ts","sourceRoot":"","sources":["../src/addon-loader.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAGjE,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;QAClB,wBAAwB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;QAC3D,wBAAwB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACrD;CACJ;AAED,MAAM,WAAW,gBAAgB;IAC7B,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAA;IACb,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAA;IACX,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+DAA+D;IAC/D,GAAG,EAAE,QAAQ,CAAA;IACb,wCAAwC;IACxC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;IAC9B;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC7B;AAqFD,wBAAgB,WAAW,CAAC,EACxB,KAAK,EACL,GAAG,EACH,MAAqB,EACrB,GAAG,EACH,QAAe,EACf,OAAO,EACP,OAAO,EACP,MAAM,EACN,QAAQ,GACX,EAAE,gBAAgB,2CAoClB"}
|
package/dist/addon-loader.js
CHANGED
|
@@ -4,6 +4,10 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
4
4
|
// addon's `register(api)` export with the AddonAPI injected by the host.
|
|
5
5
|
import { useEffect, useRef, useState } from 'react';
|
|
6
6
|
import { useDeclareAddonLayout } from './addon-layout-context';
|
|
7
|
+
// Runtime dynamic import of an external URL. Wrapped in `new Function` so no
|
|
8
|
+
// build tool (tsc here, Vite in the consuming host) tries to statically
|
|
9
|
+
// analyse or rewrite the import — it stays a genuine runtime ESM fetch.
|
|
10
|
+
const importModule = new Function('u', 'return import(u)');
|
|
7
11
|
const loadedScripts = new Map();
|
|
8
12
|
function loadScript(url, scope) {
|
|
9
13
|
const key = `${scope}::${url}`;
|
|
@@ -22,15 +26,53 @@ function loadScript(url, scope) {
|
|
|
22
26
|
loadedScripts.set(key, promise);
|
|
23
27
|
return promise;
|
|
24
28
|
}
|
|
25
|
-
|
|
29
|
+
const esmContainers = new Map();
|
|
30
|
+
// Resolve a federation container for the remote. Vite/@originjs remotes built
|
|
31
|
+
// with `format:"esm"` (our standard) are ES modules that top-level `import`
|
|
32
|
+
// their preload helper and export `{ init, get }` — they MUST be loaded as a
|
|
33
|
+
// module (a classic <script> throws "Cannot use import statement outside a
|
|
34
|
+
// module"), so we dynamic-import them and use the module namespace as the
|
|
35
|
+
// container. Legacy "var"/window remotes (which assign `window[scope]`) are
|
|
36
|
+
// still supported via the classic <script> fallback.
|
|
37
|
+
async function resolveContainer(scope, url) {
|
|
38
|
+
const key = `${scope}::${url}`;
|
|
39
|
+
const cached = esmContainers.get(key);
|
|
40
|
+
if (cached)
|
|
41
|
+
return cached;
|
|
42
|
+
const p = (async () => {
|
|
43
|
+
try {
|
|
44
|
+
const mod = await importModule(url);
|
|
45
|
+
if (mod && typeof mod.init === 'function' && typeof mod.get === 'function') {
|
|
46
|
+
return mod;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Not an importable module (legacy var-format remote) — fall back.
|
|
51
|
+
}
|
|
52
|
+
await loadScript(url, scope);
|
|
53
|
+
return window[scope];
|
|
54
|
+
})();
|
|
55
|
+
esmContainers.set(key, p);
|
|
56
|
+
p.catch(() => esmContainers.delete(key));
|
|
57
|
+
return p;
|
|
58
|
+
}
|
|
59
|
+
async function loadRemote(scope, url, module) {
|
|
26
60
|
if (typeof window.__webpack_init_sharing__ === 'function') {
|
|
27
61
|
await window.__webpack_init_sharing__('default');
|
|
28
62
|
}
|
|
29
|
-
const container =
|
|
30
|
-
if (!container)
|
|
31
|
-
throw new Error(`Addon container "${scope}" not found
|
|
32
|
-
|
|
33
|
-
|
|
63
|
+
const container = await resolveContainer(scope, url);
|
|
64
|
+
if (!container) {
|
|
65
|
+
throw new Error(`Addon container "${scope}" not found (neither ESM export nor window[scope])`);
|
|
66
|
+
}
|
|
67
|
+
if (typeof container.init === 'function') {
|
|
68
|
+
const shareScope = window.__webpack_share_scopes__?.default ??
|
|
69
|
+
(window.__METACORE_SHARE_SCOPE__ ??= {});
|
|
70
|
+
try {
|
|
71
|
+
await container.init(shareScope);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Container already initialized (re-entrant load) — safe to ignore.
|
|
75
|
+
}
|
|
34
76
|
}
|
|
35
77
|
const factory = await container.get(module);
|
|
36
78
|
return factory();
|
|
@@ -48,10 +90,7 @@ export function AddonLoader({ scope, url, module = './register', api, fallback =
|
|
|
48
90
|
let cancelled = false;
|
|
49
91
|
(async () => {
|
|
50
92
|
try {
|
|
51
|
-
await
|
|
52
|
-
if (cancelled)
|
|
53
|
-
return;
|
|
54
|
-
const mod = await loadRemote(scope, module);
|
|
93
|
+
const mod = await loadRemote(scope, url, module);
|
|
55
94
|
if (cancelled)
|
|
56
95
|
return;
|
|
57
96
|
if (!didRegister.current && typeof mod?.register === 'function') {
|
package/package.json
CHANGED
package/src/addon-loader.tsx
CHANGED
|
@@ -43,6 +43,18 @@ export interface AddonLoaderProps {
|
|
|
43
43
|
children?: React.ReactNode
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
interface FederationContainer {
|
|
47
|
+
init: (shareScope: unknown) => Promise<void>
|
|
48
|
+
get: (module: string) => Promise<() => any>
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Runtime dynamic import of an external URL. Wrapped in `new Function` so no
|
|
52
|
+
// build tool (tsc here, Vite in the consuming host) tries to statically
|
|
53
|
+
// analyse or rewrite the import — it stays a genuine runtime ESM fetch.
|
|
54
|
+
const importModule = new Function('u', 'return import(u)') as (
|
|
55
|
+
u: string,
|
|
56
|
+
) => Promise<Record<string, unknown>>
|
|
57
|
+
|
|
46
58
|
const loadedScripts = new Map<string, Promise<void>>()
|
|
47
59
|
|
|
48
60
|
function loadScript(url: string, scope: string): Promise<void> {
|
|
@@ -62,19 +74,53 @@ function loadScript(url: string, scope: string): Promise<void> {
|
|
|
62
74
|
return promise
|
|
63
75
|
}
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
const esmContainers = new Map<string, Promise<FederationContainer | undefined>>()
|
|
78
|
+
|
|
79
|
+
// Resolve a federation container for the remote. Vite/@originjs remotes built
|
|
80
|
+
// with `format:"esm"` (our standard) are ES modules that top-level `import`
|
|
81
|
+
// their preload helper and export `{ init, get }` — they MUST be loaded as a
|
|
82
|
+
// module (a classic <script> throws "Cannot use import statement outside a
|
|
83
|
+
// module"), so we dynamic-import them and use the module namespace as the
|
|
84
|
+
// container. Legacy "var"/window remotes (which assign `window[scope]`) are
|
|
85
|
+
// still supported via the classic <script> fallback.
|
|
86
|
+
async function resolveContainer(scope: string, url: string): Promise<FederationContainer | undefined> {
|
|
87
|
+
const key = `${scope}::${url}`
|
|
88
|
+
const cached = esmContainers.get(key)
|
|
89
|
+
if (cached) return cached
|
|
90
|
+
const p = (async () => {
|
|
91
|
+
try {
|
|
92
|
+
const mod = await importModule(url)
|
|
93
|
+
if (mod && typeof mod.init === 'function' && typeof mod.get === 'function') {
|
|
94
|
+
return mod as unknown as FederationContainer
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
// Not an importable module (legacy var-format remote) — fall back.
|
|
98
|
+
}
|
|
99
|
+
await loadScript(url, scope)
|
|
100
|
+
return (window as any)[scope] as FederationContainer | undefined
|
|
101
|
+
})()
|
|
102
|
+
esmContainers.set(key, p)
|
|
103
|
+
p.catch(() => esmContainers.delete(key))
|
|
104
|
+
return p
|
|
68
105
|
}
|
|
69
106
|
|
|
70
|
-
async function loadRemote(scope: string, module: string) {
|
|
107
|
+
async function loadRemote(scope: string, url: string, module: string) {
|
|
71
108
|
if (typeof window.__webpack_init_sharing__ === 'function') {
|
|
72
109
|
await window.__webpack_init_sharing__('default')
|
|
73
110
|
}
|
|
74
|
-
const container =
|
|
75
|
-
if (!container)
|
|
76
|
-
|
|
77
|
-
|
|
111
|
+
const container = await resolveContainer(scope, url)
|
|
112
|
+
if (!container) {
|
|
113
|
+
throw new Error(`Addon container "${scope}" not found (neither ESM export nor window[scope])`)
|
|
114
|
+
}
|
|
115
|
+
if (typeof container.init === 'function') {
|
|
116
|
+
const shareScope =
|
|
117
|
+
window.__webpack_share_scopes__?.default ??
|
|
118
|
+
((window as any).__METACORE_SHARE_SCOPE__ ??= {})
|
|
119
|
+
try {
|
|
120
|
+
await container.init(shareScope)
|
|
121
|
+
} catch {
|
|
122
|
+
// Container already initialized (re-entrant load) — safe to ignore.
|
|
123
|
+
}
|
|
78
124
|
}
|
|
79
125
|
const factory = await container.get(module)
|
|
80
126
|
return factory()
|
|
@@ -105,9 +151,7 @@ export function AddonLoader({
|
|
|
105
151
|
let cancelled = false
|
|
106
152
|
;(async () => {
|
|
107
153
|
try {
|
|
108
|
-
await
|
|
109
|
-
if (cancelled) return
|
|
110
|
-
const mod = await loadRemote(scope, module)
|
|
154
|
+
const mod = await loadRemote(scope, url, module)
|
|
111
155
|
if (cancelled) return
|
|
112
156
|
if (!didRegister.current && typeof mod?.register === 'function') {
|
|
113
157
|
didRegister.current = true
|