@omriashke/dynamico-core 0.1.6 → 0.1.7
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/registry.d.ts +6 -9
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +23 -25
- package/dist/registry.js.map +1 -1
- package/dist/relativeRequires.d.ts +7 -0
- package/dist/relativeRequires.d.ts.map +1 -0
- package/dist/relativeRequires.js +40 -0
- package/dist/relativeRequires.js.map +1 -0
- package/package.json +1 -1
- package/src/registry.ts +24 -26
- package/src/relativeRequires.ts +39 -0
package/dist/registry.d.ts
CHANGED
|
@@ -39,23 +39,20 @@ export declare class Registry {
|
|
|
39
39
|
* Cross-component imports look up other components by name in the registry.
|
|
40
40
|
* v1: we map "./Other" -> "Other" (basename, no extension).
|
|
41
41
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* - Card can `require("./Hello")` at eval time even before Hello has
|
|
47
|
-
* loaded — the proxy is returned immediately.
|
|
48
|
-
* - When Hello actually arrives (or hot-swaps to a new version), Card's
|
|
49
|
-
* next render automatically picks it up; no manual preload needed.
|
|
42
|
+
* Returns a lazy proxy module. When the dependency is loaded, non-component
|
|
43
|
+
* exports (e.g. `Colors`) and hooks resolve to their real values so module-
|
|
44
|
+
* level reads like `Colors.primary` work. Components not yet loaded still
|
|
45
|
+
* get lazy wrappers that re-resolve on each render for hot-swap.
|
|
50
46
|
*/
|
|
51
47
|
requireByPath(specifier: string): unknown;
|
|
52
48
|
private lazyProxies;
|
|
49
|
+
private resolveExport;
|
|
53
50
|
private makeLazyProxy;
|
|
54
51
|
/**
|
|
55
52
|
* Take a CompiledModule from the source, evaluate it (or record a compile
|
|
56
53
|
* error), update the entry, and notify listeners.
|
|
57
54
|
*/
|
|
58
|
-
private
|
|
55
|
+
private ingestAsync;
|
|
59
56
|
private notify;
|
|
60
57
|
}
|
|
61
58
|
//# sourceMappingURL=registry.d.ts.map
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,aAAa,EACb,gBAAgB,EAChB,KAAK,EACL,MAAM,EAEP,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,aAAa,EACb,gBAAgB,EAChB,KAAK,EACL,MAAM,EAEP,MAAM,YAAY,CAAC;AAIpB;;;;;;;GAOG;AACH,qBAAa,QAAQ;IAOjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,KAAK;IAPf,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,SAAS,CAA4C;IAC7D,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,QAAQ,CAA6C;gBAG1C,MAAM,EAAE,MAAM,EACvB,KAAK,EAAE,KAAK;IAOtB,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAI5B;;;;OAIG;IACH,QAAQ,IAAI,KAAK;IAIjB,gDAAgD;IAChD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI7C;;;OAGG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAelD,qDAAqD;IACrD,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,MAAM,IAAI;IAa/D,kEAAkE;IAClE,YAAY,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,IAAI;IAOpD;;;;;;;;;OASG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAezC,OAAO,CAAC,WAAW,CAA8C;IAEjE,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;IAiCrB;;;OAGG;YACW,WAAW;IAuDzB,OAAO,CAAC,MAAM;CAoBf"}
|
package/dist/registry.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { loadModule } from "./loader.js";
|
|
2
|
+
import { collectRelativeComponentDeps } from "./relativeRequires.js";
|
|
2
3
|
/**
|
|
3
4
|
* In-memory, versioned registry of dynamic components.
|
|
4
5
|
*
|
|
@@ -17,7 +18,7 @@ export class Registry {
|
|
|
17
18
|
this.inflight = new Map();
|
|
18
19
|
this.lazyProxies = new Map();
|
|
19
20
|
this.source.subscribe(({ module }) => {
|
|
20
|
-
this.
|
|
21
|
+
void this.ingestAsync(module.name, module);
|
|
21
22
|
});
|
|
22
23
|
}
|
|
23
24
|
/** Replace or extend the current scope (rare; typically set once). */
|
|
@@ -49,7 +50,7 @@ export class Registry {
|
|
|
49
50
|
return pending;
|
|
50
51
|
const p = this.source
|
|
51
52
|
.fetch(name)
|
|
52
|
-
.then((module) => this.
|
|
53
|
+
.then((module) => this.ingestAsync(name, module))
|
|
53
54
|
.finally(() => {
|
|
54
55
|
this.inflight.delete(name);
|
|
55
56
|
});
|
|
@@ -82,14 +83,10 @@ export class Registry {
|
|
|
82
83
|
* Cross-component imports look up other components by name in the registry.
|
|
83
84
|
* v1: we map "./Other" -> "Other" (basename, no extension).
|
|
84
85
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* - Card can `require("./Hello")` at eval time even before Hello has
|
|
90
|
-
* loaded — the proxy is returned immediately.
|
|
91
|
-
* - When Hello actually arrives (or hot-swaps to a new version), Card's
|
|
92
|
-
* next render automatically picks it up; no manual preload needed.
|
|
86
|
+
* Returns a lazy proxy module. When the dependency is loaded, non-component
|
|
87
|
+
* exports (e.g. `Colors`) and hooks resolve to their real values so module-
|
|
88
|
+
* level reads like `Colors.primary` work. Components not yet loaded still
|
|
89
|
+
* get lazy wrappers that re-resolve on each render for hot-swap.
|
|
93
90
|
*/
|
|
94
91
|
requireByPath(specifier) {
|
|
95
92
|
const base = specifier
|
|
@@ -105,6 +102,14 @@ export class Registry {
|
|
|
105
102
|
}
|
|
106
103
|
return this.makeLazyProxy(base);
|
|
107
104
|
}
|
|
105
|
+
resolveExport(name, key) {
|
|
106
|
+
const entry = this.entries.get(name);
|
|
107
|
+
if (!entry?.factory || entry.error)
|
|
108
|
+
return undefined;
|
|
109
|
+
if (!Object.prototype.hasOwnProperty.call(entry.factory, key))
|
|
110
|
+
return undefined;
|
|
111
|
+
return entry.factory[key];
|
|
112
|
+
}
|
|
108
113
|
makeLazyProxy(name) {
|
|
109
114
|
const cached = this.lazyProxies.get(name);
|
|
110
115
|
if (cached)
|
|
@@ -114,35 +119,25 @@ export class Registry {
|
|
|
114
119
|
const make = (key) => {
|
|
115
120
|
const Comp = function LazyDynamic(props) {
|
|
116
121
|
const entry = registry.entries.get(name);
|
|
117
|
-
// Not loaded yet — render nothing. When the dependency arrives, the
|
|
118
|
-
// cross-dep notification in `notify()` refreshes our parent's entry,
|
|
119
|
-
// which causes useSyncExternalStore to re-render and we'll resolve
|
|
120
|
-
// for real on the next pass.
|
|
121
122
|
if (!entry || (!entry.factory && !entry.error))
|
|
122
123
|
return null;
|
|
123
|
-
if (entry.error)
|
|
124
|
-
// Surface the dep error inline. Parent can wrap in errorFallback at
|
|
125
|
-
// its own level if it wants; we don't have access to it here.
|
|
124
|
+
if (entry.error)
|
|
126
125
|
return null;
|
|
127
|
-
}
|
|
128
126
|
const target = entry.factory?.[key];
|
|
129
127
|
if (typeof target !== "function")
|
|
130
128
|
return null;
|
|
131
|
-
// The host-scope's React.createElement is what invoked us; we just
|
|
132
|
-
// call the real component function. props is what the parent passed.
|
|
133
129
|
return target(props);
|
|
134
130
|
};
|
|
135
131
|
Object.defineProperty(Comp, "name", { value: `Lazy(${name}.${key})` });
|
|
136
132
|
return Comp;
|
|
137
133
|
};
|
|
138
|
-
proxy.default = make("default");
|
|
139
|
-
// Allow named imports via Proxy: any key access returns a fresh lazy
|
|
140
|
-
// component bound to that key. Default is set above; everything else
|
|
141
|
-
// is created on demand.
|
|
142
134
|
const handler = {
|
|
143
135
|
get(target, prop) {
|
|
144
136
|
if (typeof prop !== "string")
|
|
145
137
|
return undefined;
|
|
138
|
+
const resolved = registry.resolveExport(name, prop);
|
|
139
|
+
if (resolved !== undefined)
|
|
140
|
+
return resolved;
|
|
146
141
|
if (prop in target)
|
|
147
142
|
return target[prop];
|
|
148
143
|
const c = make(prop);
|
|
@@ -158,7 +153,7 @@ export class Registry {
|
|
|
158
153
|
* Take a CompiledModule from the source, evaluate it (or record a compile
|
|
159
154
|
* error), update the entry, and notify listeners.
|
|
160
155
|
*/
|
|
161
|
-
|
|
156
|
+
async ingestAsync(name, module) {
|
|
162
157
|
if (module.removed) {
|
|
163
158
|
this.entries.delete(name);
|
|
164
159
|
this.lazyProxies.delete(name);
|
|
@@ -191,6 +186,9 @@ export class Registry {
|
|
|
191
186
|
}
|
|
192
187
|
else {
|
|
193
188
|
try {
|
|
189
|
+
const deps = collectRelativeComponentDeps(module.code, name);
|
|
190
|
+
await Promise.all(deps.map((dep) => this.ensure(dep)));
|
|
191
|
+
this.lazyProxies.delete(name);
|
|
194
192
|
const factory = loadModule(module.code, this.scope, (rel) => this.requireByPath(rel));
|
|
195
193
|
entry = { name, version: module.version, factory };
|
|
196
194
|
}
|
package/dist/registry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Map a relative require specifier to the flat registry component name (basename). */
|
|
2
|
+
export declare function resolveRelativeComponentName(specifier: string): string | null;
|
|
3
|
+
/** Collect relative require() specifiers from compiled CommonJS output. */
|
|
4
|
+
export declare function extractRelativeRequires(code: string): string[];
|
|
5
|
+
/** Registry component names referenced by relative imports in compiled code. */
|
|
6
|
+
export declare function collectRelativeComponentDeps(code: string, componentName?: string): string[];
|
|
7
|
+
//# sourceMappingURL=relativeRequires.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relativeRequires.d.ts","sourceRoot":"","sources":["../src/relativeRequires.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU7E;AAED,2EAA2E;AAC3E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAa9D;AAED,gFAAgF;AAChF,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ3F"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Map a relative require specifier to the flat registry component name (basename). */
|
|
2
|
+
export function resolveRelativeComponentName(specifier) {
|
|
3
|
+
if (!specifier.startsWith("./") && !specifier.startsWith("../") && !specifier.startsWith("/")) {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
const base = specifier
|
|
7
|
+
.replace(/^\.+\//, "")
|
|
8
|
+
.replace(/\.[tj]sx?$/, "")
|
|
9
|
+
.split("/")
|
|
10
|
+
.pop();
|
|
11
|
+
return base || null;
|
|
12
|
+
}
|
|
13
|
+
/** Collect relative require() specifiers from compiled CommonJS output. */
|
|
14
|
+
export function extractRelativeRequires(code) {
|
|
15
|
+
const found = new Set();
|
|
16
|
+
const patterns = [
|
|
17
|
+
/require\(\s*["'](\.[^"']+)["']\s*\)/g,
|
|
18
|
+
/require\(\s*["'](\.\.[^"']+)["']\s*\)/g,
|
|
19
|
+
];
|
|
20
|
+
for (const re of patterns) {
|
|
21
|
+
for (const match of code.matchAll(re)) {
|
|
22
|
+
const spec = match[1];
|
|
23
|
+
if (spec)
|
|
24
|
+
found.add(spec);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return [...found];
|
|
28
|
+
}
|
|
29
|
+
/** Registry component names referenced by relative imports in compiled code. */
|
|
30
|
+
export function collectRelativeComponentDeps(code, componentName) {
|
|
31
|
+
const deps = new Set();
|
|
32
|
+
for (const specifier of extractRelativeRequires(code)) {
|
|
33
|
+
const base = resolveRelativeComponentName(specifier);
|
|
34
|
+
if (!base || base === componentName)
|
|
35
|
+
continue;
|
|
36
|
+
deps.add(base);
|
|
37
|
+
}
|
|
38
|
+
return [...deps];
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=relativeRequires.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relativeRequires.js","sourceRoot":"","sources":["../src/relativeRequires.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,MAAM,UAAU,4BAA4B,CAAC,SAAiB;IAC5D,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9F,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,IAAI,GAAG,SAAS;SACnB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;SACzB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,EAAE,CAAC;IACT,OAAO,IAAI,IAAI,IAAI,CAAC;AACtB,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,MAAM,QAAQ,GAAG;QACf,sCAAsC;QACtC,wCAAwC;KACzC,CAAC;IACF,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC1B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,IAAI;gBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC;AACpB,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,4BAA4B,CAAC,IAAY,EAAE,aAAsB;IAC/E,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,SAAS,IAAI,uBAAuB,CAAC,IAAI,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,GAAG,4BAA4B,CAAC,SAAS,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,aAAa;YAAE,SAAS;QAC9C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;AACnB,CAAC"}
|
package/package.json
CHANGED
package/src/registry.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
Version,
|
|
9
9
|
} from "./types.js";
|
|
10
10
|
import { loadModule } from "./loader.js";
|
|
11
|
+
import { collectRelativeComponentDeps } from "./relativeRequires.js";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* In-memory, versioned registry of dynamic components.
|
|
@@ -28,7 +29,7 @@ export class Registry {
|
|
|
28
29
|
private scope: Scope,
|
|
29
30
|
) {
|
|
30
31
|
this.source.subscribe(({ module }) => {
|
|
31
|
-
this.
|
|
32
|
+
void this.ingestAsync(module.name, module);
|
|
32
33
|
});
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -62,7 +63,7 @@ export class Registry {
|
|
|
62
63
|
if (pending) return pending;
|
|
63
64
|
const p = this.source
|
|
64
65
|
.fetch(name)
|
|
65
|
-
.then((module) => this.
|
|
66
|
+
.then((module) => this.ingestAsync(name, module))
|
|
66
67
|
.finally(() => {
|
|
67
68
|
this.inflight.delete(name);
|
|
68
69
|
});
|
|
@@ -97,14 +98,10 @@ export class Registry {
|
|
|
97
98
|
* Cross-component imports look up other components by name in the registry.
|
|
98
99
|
* v1: we map "./Other" -> "Other" (basename, no extension).
|
|
99
100
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
* - Card can `require("./Hello")` at eval time even before Hello has
|
|
105
|
-
* loaded — the proxy is returned immediately.
|
|
106
|
-
* - When Hello actually arrives (or hot-swaps to a new version), Card's
|
|
107
|
-
* next render automatically picks it up; no manual preload needed.
|
|
101
|
+
* Returns a lazy proxy module. When the dependency is loaded, non-component
|
|
102
|
+
* exports (e.g. `Colors`) and hooks resolve to their real values so module-
|
|
103
|
+
* level reads like `Colors.primary` work. Components not yet loaded still
|
|
104
|
+
* get lazy wrappers that re-resolve on each render for hot-swap.
|
|
108
105
|
*/
|
|
109
106
|
requireByPath(specifier: string): unknown {
|
|
110
107
|
const base = specifier
|
|
@@ -123,6 +120,13 @@ export class Registry {
|
|
|
123
120
|
|
|
124
121
|
private lazyProxies = new Map<string, Record<string, unknown>>();
|
|
125
122
|
|
|
123
|
+
private resolveExport(name: string, key: string): unknown | undefined {
|
|
124
|
+
const entry = this.entries.get(name);
|
|
125
|
+
if (!entry?.factory || entry.error) return undefined;
|
|
126
|
+
if (!Object.prototype.hasOwnProperty.call(entry.factory, key)) return undefined;
|
|
127
|
+
return entry.factory[key];
|
|
128
|
+
}
|
|
129
|
+
|
|
126
130
|
private makeLazyProxy(name: string): Record<string, unknown> {
|
|
127
131
|
const cached = this.lazyProxies.get(name);
|
|
128
132
|
if (cached) return cached;
|
|
@@ -131,32 +135,20 @@ export class Registry {
|
|
|
131
135
|
const make = (key: string) => {
|
|
132
136
|
const Comp = function LazyDynamic(props: Record<string, unknown>) {
|
|
133
137
|
const entry = registry.entries.get(name);
|
|
134
|
-
// Not loaded yet — render nothing. When the dependency arrives, the
|
|
135
|
-
// cross-dep notification in `notify()` refreshes our parent's entry,
|
|
136
|
-
// which causes useSyncExternalStore to re-render and we'll resolve
|
|
137
|
-
// for real on the next pass.
|
|
138
138
|
if (!entry || (!entry.factory && !entry.error)) return null;
|
|
139
|
-
if (entry.error)
|
|
140
|
-
// Surface the dep error inline. Parent can wrap in errorFallback at
|
|
141
|
-
// its own level if it wants; we don't have access to it here.
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
139
|
+
if (entry.error) return null;
|
|
144
140
|
const target = entry.factory?.[key];
|
|
145
141
|
if (typeof target !== "function") return null;
|
|
146
|
-
// The host-scope's React.createElement is what invoked us; we just
|
|
147
|
-
// call the real component function. props is what the parent passed.
|
|
148
142
|
return (target as (p: Record<string, unknown>) => unknown)(props);
|
|
149
143
|
};
|
|
150
144
|
Object.defineProperty(Comp, "name", { value: `Lazy(${name}.${key})` });
|
|
151
145
|
return Comp;
|
|
152
146
|
};
|
|
153
|
-
proxy.default = make("default");
|
|
154
|
-
// Allow named imports via Proxy: any key access returns a fresh lazy
|
|
155
|
-
// component bound to that key. Default is set above; everything else
|
|
156
|
-
// is created on demand.
|
|
157
147
|
const handler: ProxyHandler<Record<string, unknown>> = {
|
|
158
148
|
get(target, prop) {
|
|
159
149
|
if (typeof prop !== "string") return undefined;
|
|
150
|
+
const resolved = registry.resolveExport(name, prop);
|
|
151
|
+
if (resolved !== undefined) return resolved;
|
|
160
152
|
if (prop in target) return target[prop];
|
|
161
153
|
const c = make(prop);
|
|
162
154
|
target[prop] = c;
|
|
@@ -172,7 +164,10 @@ export class Registry {
|
|
|
172
164
|
* Take a CompiledModule from the source, evaluate it (or record a compile
|
|
173
165
|
* error), update the entry, and notify listeners.
|
|
174
166
|
*/
|
|
175
|
-
private
|
|
167
|
+
private async ingestAsync(
|
|
168
|
+
name: string,
|
|
169
|
+
module: import("./types.js").CompiledModule,
|
|
170
|
+
): Promise<RegistryEntry> {
|
|
176
171
|
if (module.removed) {
|
|
177
172
|
this.entries.delete(name);
|
|
178
173
|
this.lazyProxies.delete(name);
|
|
@@ -204,6 +199,9 @@ export class Registry {
|
|
|
204
199
|
};
|
|
205
200
|
} else {
|
|
206
201
|
try {
|
|
202
|
+
const deps = collectRelativeComponentDeps(module.code, name);
|
|
203
|
+
await Promise.all(deps.map((dep) => this.ensure(dep)));
|
|
204
|
+
this.lazyProxies.delete(name);
|
|
207
205
|
const factory = loadModule(module.code, this.scope, (rel) =>
|
|
208
206
|
this.requireByPath(rel),
|
|
209
207
|
) as ComponentFactory;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Map a relative require specifier to the flat registry component name (basename). */
|
|
2
|
+
export function resolveRelativeComponentName(specifier: string): string | null {
|
|
3
|
+
if (!specifier.startsWith("./") && !specifier.startsWith("../") && !specifier.startsWith("/")) {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
const base = specifier
|
|
7
|
+
.replace(/^\.+\//, "")
|
|
8
|
+
.replace(/\.[tj]sx?$/, "")
|
|
9
|
+
.split("/")
|
|
10
|
+
.pop();
|
|
11
|
+
return base || null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Collect relative require() specifiers from compiled CommonJS output. */
|
|
15
|
+
export function extractRelativeRequires(code: string): string[] {
|
|
16
|
+
const found = new Set<string>();
|
|
17
|
+
const patterns = [
|
|
18
|
+
/require\(\s*["'](\.[^"']+)["']\s*\)/g,
|
|
19
|
+
/require\(\s*["'](\.\.[^"']+)["']\s*\)/g,
|
|
20
|
+
];
|
|
21
|
+
for (const re of patterns) {
|
|
22
|
+
for (const match of code.matchAll(re)) {
|
|
23
|
+
const spec = match[1];
|
|
24
|
+
if (spec) found.add(spec);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return [...found];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Registry component names referenced by relative imports in compiled code. */
|
|
31
|
+
export function collectRelativeComponentDeps(code: string, componentName?: string): string[] {
|
|
32
|
+
const deps = new Set<string>();
|
|
33
|
+
for (const specifier of extractRelativeRequires(code)) {
|
|
34
|
+
const base = resolveRelativeComponentName(specifier);
|
|
35
|
+
if (!base || base === componentName) continue;
|
|
36
|
+
deps.add(base);
|
|
37
|
+
}
|
|
38
|
+
return [...deps];
|
|
39
|
+
}
|