sandlot 0.1.1 → 0.1.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/README.md +145 -518
- package/dist/build-emitter.d.ts +47 -0
- package/dist/build-emitter.d.ts.map +1 -0
- package/dist/builder.d.ts +370 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/bundler.d.ts +3 -3
- package/dist/bundler.d.ts.map +1 -1
- package/dist/commands/compile.d.ts +13 -0
- package/dist/commands/compile.d.ts.map +1 -0
- package/dist/commands/index.d.ts +17 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/packages.d.ts +17 -0
- package/dist/commands/packages.d.ts.map +1 -0
- package/dist/commands/run.d.ts +40 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/types.d.ts +141 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/fs.d.ts +60 -42
- package/dist/fs.d.ts.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +304 -491
- package/dist/internal.d.ts +5 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +174 -95
- package/dist/runner.d.ts +314 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/sandbox-manager.d.ts +45 -33
- package/dist/sandbox-manager.d.ts.map +1 -1
- package/dist/sandbox.d.ts +144 -70
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/shared-modules.d.ts +22 -3
- package/dist/shared-modules.d.ts.map +1 -1
- package/dist/shared-resources.d.ts +0 -3
- package/dist/shared-resources.d.ts.map +1 -1
- package/dist/typechecker.d.ts +1 -1
- package/package.json +3 -17
- package/src/build-emitter.ts +64 -0
- package/src/builder.ts +498 -0
- package/src/bundler.ts +86 -57
- package/src/commands/compile.ts +236 -0
- package/src/commands/index.ts +51 -0
- package/src/commands/packages.ts +154 -0
- package/src/commands/run.ts +245 -0
- package/src/commands/types.ts +172 -0
- package/src/fs.ts +90 -216
- package/src/index.ts +34 -12
- package/src/internal.ts +5 -2
- package/src/sandbox.ts +214 -220
- package/src/shared-modules.ts +74 -4
- package/src/shared-resources.ts +0 -3
- package/src/ts-libs.ts +1 -1
- package/src/typechecker.ts +1 -1
- package/dist/react.d.ts +0 -159
- package/dist/react.d.ts.map +0 -1
- package/dist/react.js +0 -149
- package/src/commands.ts +0 -733
- package/src/react.tsx +0 -331
- package/src/sandbox-manager.ts +0 -490
package/src/shared-modules.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* });
|
|
22
22
|
*
|
|
23
23
|
* // Now create sandbox with sharedModules option
|
|
24
|
-
* const sandbox = await
|
|
24
|
+
* const sandbox = await createSandbox({
|
|
25
25
|
* sharedModules: ['react', 'react-dom/client'],
|
|
26
26
|
* });
|
|
27
27
|
* ```
|
|
@@ -39,6 +39,7 @@ const GLOBAL_KEY = '__sandlot_shared_modules__';
|
|
|
39
39
|
*/
|
|
40
40
|
export class SharedModuleRegistry {
|
|
41
41
|
private modules = new Map<string, unknown>();
|
|
42
|
+
private exportNames = new Map<string, string[]>();
|
|
42
43
|
|
|
43
44
|
constructor() {
|
|
44
45
|
// Make available globally for dynamic imports
|
|
@@ -46,7 +47,8 @@ export class SharedModuleRegistry {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
/**
|
|
49
|
-
* Register a module to be shared with dynamic bundles
|
|
50
|
+
* Register a module to be shared with dynamic bundles.
|
|
51
|
+
* Automatically introspects the module to discover its exports.
|
|
50
52
|
*
|
|
51
53
|
* @param moduleId - The import specifier (e.g., 'react', 'react-dom/client')
|
|
52
54
|
* @param module - The module's exports object
|
|
@@ -54,18 +56,21 @@ export class SharedModuleRegistry {
|
|
|
54
56
|
*/
|
|
55
57
|
register(moduleId: string, module: unknown): this {
|
|
56
58
|
this.modules.set(moduleId, module);
|
|
59
|
+
// Introspect the module to get its export names
|
|
60
|
+
this.exportNames.set(moduleId, introspectExports(module));
|
|
57
61
|
return this;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
/**
|
|
61
|
-
* Register multiple modules at once
|
|
65
|
+
* Register multiple modules at once.
|
|
66
|
+
* Automatically introspects each module to discover its exports.
|
|
62
67
|
*
|
|
63
68
|
* @param modules - Object mapping module IDs to their exports
|
|
64
69
|
* @returns this for chaining
|
|
65
70
|
*/
|
|
66
71
|
registerAll(modules: Record<string, unknown>): this {
|
|
67
72
|
for (const [id, mod] of Object.entries(modules)) {
|
|
68
|
-
this.
|
|
73
|
+
this.register(id, mod);
|
|
69
74
|
}
|
|
70
75
|
return this;
|
|
71
76
|
}
|
|
@@ -116,11 +121,23 @@ export class SharedModuleRegistry {
|
|
|
116
121
|
return [...this.modules.keys()];
|
|
117
122
|
}
|
|
118
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Get the export names for a registered module.
|
|
126
|
+
* These were discovered by introspecting the module at registration time.
|
|
127
|
+
*
|
|
128
|
+
* @param moduleId - The import specifier
|
|
129
|
+
* @returns Array of export names, or empty array if not registered
|
|
130
|
+
*/
|
|
131
|
+
getExportNames(moduleId: string): string[] {
|
|
132
|
+
return this.exportNames.get(moduleId) ?? [];
|
|
133
|
+
}
|
|
134
|
+
|
|
119
135
|
/**
|
|
120
136
|
* Clear all registrations
|
|
121
137
|
*/
|
|
122
138
|
clear(): void {
|
|
123
139
|
this.modules.clear();
|
|
140
|
+
this.exportNames.clear();
|
|
124
141
|
}
|
|
125
142
|
|
|
126
143
|
/**
|
|
@@ -131,6 +148,48 @@ export class SharedModuleRegistry {
|
|
|
131
148
|
}
|
|
132
149
|
}
|
|
133
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Introspect a module to discover its export names.
|
|
153
|
+
* Filters out non-identifier keys and internal properties.
|
|
154
|
+
*/
|
|
155
|
+
function introspectExports(module: unknown): string[] {
|
|
156
|
+
if (module === null || module === undefined) {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (typeof module !== 'object' && typeof module !== 'function') {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const exports: string[] = [];
|
|
165
|
+
|
|
166
|
+
// Get own enumerable properties
|
|
167
|
+
for (const key of Object.keys(module as object)) {
|
|
168
|
+
// Filter out non-valid JavaScript identifiers
|
|
169
|
+
if (isValidIdentifier(key)) {
|
|
170
|
+
exports.push(key);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return exports;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Check if a string is a valid JavaScript identifier.
|
|
179
|
+
* Used to filter out keys that can't be used as named exports.
|
|
180
|
+
*/
|
|
181
|
+
function isValidIdentifier(name: string): boolean {
|
|
182
|
+
if (name.length === 0) return false;
|
|
183
|
+
// Must start with letter, underscore, or $
|
|
184
|
+
if (!/^[a-zA-Z_$]/.test(name)) return false;
|
|
185
|
+
// Rest must be alphanumeric, underscore, or $
|
|
186
|
+
if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) return false;
|
|
187
|
+
// Exclude reserved words that would cause issues
|
|
188
|
+
const reserved = ['default', 'class', 'function', 'var', 'let', 'const', 'import', 'export'];
|
|
189
|
+
if (reserved.includes(name)) return false;
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
|
|
134
193
|
// Singleton instance
|
|
135
194
|
let defaultRegistry: SharedModuleRegistry | null = null;
|
|
136
195
|
|
|
@@ -190,6 +249,17 @@ export function clearSharedModules(): void {
|
|
|
190
249
|
getSharedModuleRegistry().clear();
|
|
191
250
|
}
|
|
192
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Get the export names for a registered shared module.
|
|
254
|
+
* Used by the bundler to generate proper re-export statements.
|
|
255
|
+
*
|
|
256
|
+
* @param moduleId - The import specifier
|
|
257
|
+
* @returns Array of export names, or empty array if not registered
|
|
258
|
+
*/
|
|
259
|
+
export function getSharedModuleExports(moduleId: string): string[] {
|
|
260
|
+
return getSharedModuleRegistry().getExportNames(moduleId);
|
|
261
|
+
}
|
|
262
|
+
|
|
193
263
|
/**
|
|
194
264
|
* Generate the runtime code that dynamic bundles use to access shared modules.
|
|
195
265
|
* This is injected into bundles when they import from shared modules.
|
package/src/shared-resources.ts
CHANGED
|
@@ -6,9 +6,6 @@
|
|
|
6
6
|
* - esbuild WASM (~10MB) - singleton bundler initialization
|
|
7
7
|
* - Types cache - avoids redundant network fetches when multiple sandboxes
|
|
8
8
|
* install the same packages
|
|
9
|
-
*
|
|
10
|
-
* This module consolidates what was previously scattered across
|
|
11
|
-
* sandbox.ts and sandbox-manager.ts into a single source of truth.
|
|
12
9
|
*/
|
|
13
10
|
|
|
14
11
|
import { fetchAndCacheLibs, getDefaultBrowserLibs } from "./ts-libs";
|
package/src/ts-libs.ts
CHANGED
package/src/typechecker.ts
CHANGED
package/dist/react.d.ts
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* React-specific helpers for Sandlot
|
|
3
|
-
*
|
|
4
|
-
* These helpers simplify working with dynamically loaded React components,
|
|
5
|
-
* particularly when using the render function pattern for isolation.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```tsx
|
|
9
|
-
* import { DynamicMount } from 'sandlot/react';
|
|
10
|
-
*
|
|
11
|
-
* function App() {
|
|
12
|
-
* const [module, setModule] = useState(null);
|
|
13
|
-
*
|
|
14
|
-
* return (
|
|
15
|
-
* <DynamicMount
|
|
16
|
-
* module={module}
|
|
17
|
-
* props={{ name: "World" }}
|
|
18
|
-
* fallback={<div>Loading...</div>}
|
|
19
|
-
* />
|
|
20
|
-
* );
|
|
21
|
-
* }
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
import { type ReactElement, type RefObject, type CSSProperties } from "react";
|
|
25
|
-
/**
|
|
26
|
-
* Interface for dynamic modules that use the render function pattern.
|
|
27
|
-
* Dynamic components should export a `render` function that mounts
|
|
28
|
-
* the component into a container and returns a cleanup function.
|
|
29
|
-
*/
|
|
30
|
-
export interface DynamicRenderModule<P = Record<string, unknown>> {
|
|
31
|
-
/**
|
|
32
|
-
* Mount the component into a container element
|
|
33
|
-
*
|
|
34
|
-
* @param container - The DOM element to render into
|
|
35
|
-
* @param props - Props to pass to the component
|
|
36
|
-
* @returns A cleanup function to unmount the component
|
|
37
|
-
*/
|
|
38
|
-
render: (container: HTMLElement, props?: P) => (() => void) | void;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Props for the DynamicMount component
|
|
42
|
-
*/
|
|
43
|
-
export interface DynamicMountProps<P = Record<string, unknown>> {
|
|
44
|
-
/** The loaded dynamic module with a render function */
|
|
45
|
-
module: DynamicRenderModule<P> | null | undefined;
|
|
46
|
-
/** Props to pass to the dynamic component */
|
|
47
|
-
props?: P;
|
|
48
|
-
/** Optional className for the container div */
|
|
49
|
-
className?: string;
|
|
50
|
-
/** Optional style for the container div */
|
|
51
|
-
style?: CSSProperties;
|
|
52
|
-
/** Optional id for the container div */
|
|
53
|
-
id?: string;
|
|
54
|
-
/** Content to render while module is loading (null/undefined) */
|
|
55
|
-
fallback?: ReactElement | null;
|
|
56
|
-
/** Called when the dynamic component mounts */
|
|
57
|
-
onMount?: () => void;
|
|
58
|
-
/** Called when the dynamic component unmounts */
|
|
59
|
-
onUnmount?: () => void;
|
|
60
|
-
/** Called if rendering fails */
|
|
61
|
-
onError?: (error: Error) => void;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Component that mounts a dynamic module's render function into a container.
|
|
65
|
-
* Handles cleanup automatically when the module changes or unmounts.
|
|
66
|
-
*
|
|
67
|
-
* This is the recommended way to render dynamically loaded components
|
|
68
|
-
* that use the render function pattern for React instance isolation.
|
|
69
|
-
*
|
|
70
|
-
* @example
|
|
71
|
-
* ```tsx
|
|
72
|
-
* import { DynamicMount } from 'sandlot/react';
|
|
73
|
-
* import { loadModule } from 'sandlot';
|
|
74
|
-
*
|
|
75
|
-
* function App() {
|
|
76
|
-
* const [module, setModule] = useState(null);
|
|
77
|
-
*
|
|
78
|
-
* const loadComponent = async (buildResult: BundleResult) => {
|
|
79
|
-
* const mod = await loadModule(buildResult);
|
|
80
|
-
* setModule(mod);
|
|
81
|
-
* };
|
|
82
|
-
*
|
|
83
|
-
* return (
|
|
84
|
-
* <div>
|
|
85
|
-
* <button onClick={loadComponent}>Load</button>
|
|
86
|
-
* <DynamicMount
|
|
87
|
-
* module={module}
|
|
88
|
-
* props={{ count: 5 }}
|
|
89
|
-
* fallback={<div>Click to load...</div>}
|
|
90
|
-
* className="dynamic-container"
|
|
91
|
-
* />
|
|
92
|
-
* </div>
|
|
93
|
-
* );
|
|
94
|
-
* }
|
|
95
|
-
* ```
|
|
96
|
-
*/
|
|
97
|
-
export declare function DynamicMount<P = Record<string, unknown>>({ module, props, className, style, id, fallback, onMount, onUnmount, onError, }: DynamicMountProps<P>): ReactElement | null;
|
|
98
|
-
/**
|
|
99
|
-
* Result returned by useDynamicComponent hook
|
|
100
|
-
*/
|
|
101
|
-
export interface UseDynamicComponentResult {
|
|
102
|
-
/** Ref to attach to the container element */
|
|
103
|
-
containerRef: RefObject<HTMLDivElement | null>;
|
|
104
|
-
/** Whether the component is currently mounted */
|
|
105
|
-
isMounted: boolean;
|
|
106
|
-
/** Any error that occurred during mounting */
|
|
107
|
-
error: Error | null;
|
|
108
|
-
/** Manually trigger a re-render with new props */
|
|
109
|
-
update: (props?: Record<string, unknown>) => void;
|
|
110
|
-
/** Manually unmount the component */
|
|
111
|
-
unmount: () => void;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Hook for mounting dynamic modules with more control than DynamicMount.
|
|
115
|
-
* Provides access to mount state and manual control over mounting/unmounting.
|
|
116
|
-
*
|
|
117
|
-
* @param module - The dynamic module to mount
|
|
118
|
-
* @param props - Props to pass to the component
|
|
119
|
-
* @returns Object with containerRef, state, and control functions
|
|
120
|
-
*
|
|
121
|
-
* @example
|
|
122
|
-
* ```tsx
|
|
123
|
-
* import { useDynamicComponent } from 'sandlot/react';
|
|
124
|
-
*
|
|
125
|
-
* function App() {
|
|
126
|
-
* const { containerRef, isMounted, error, unmount } = useDynamicComponent(
|
|
127
|
-
* module,
|
|
128
|
-
* { initialCount: 0 }
|
|
129
|
-
* );
|
|
130
|
-
*
|
|
131
|
-
* return (
|
|
132
|
-
* <div>
|
|
133
|
-
* <div ref={containerRef} />
|
|
134
|
-
* {isMounted && <button onClick={unmount}>Remove</button>}
|
|
135
|
-
* {error && <div>Error: {error.message}</div>}
|
|
136
|
-
* </div>
|
|
137
|
-
* );
|
|
138
|
-
* }
|
|
139
|
-
* ```
|
|
140
|
-
*/
|
|
141
|
-
export declare function useDynamicComponent<P = Record<string, unknown>>(module: DynamicRenderModule<P> | null | undefined, props?: P): UseDynamicComponentResult;
|
|
142
|
-
/**
|
|
143
|
-
* Code template for a React component's render function.
|
|
144
|
-
* This is the pattern dynamic components should follow when using
|
|
145
|
-
* the render function pattern for React instance isolation.
|
|
146
|
-
*
|
|
147
|
-
* Dynamic components import React from esm.sh and use their own
|
|
148
|
-
* ReactDOM.createRoot to mount, avoiding conflicts with the host's React.
|
|
149
|
-
*/
|
|
150
|
-
export declare const REACT_RENDER_TEMPLATE: string;
|
|
151
|
-
/**
|
|
152
|
-
* Generate render function code for a given component name.
|
|
153
|
-
* Useful for code generation tools or agents.
|
|
154
|
-
*
|
|
155
|
-
* @param componentName - The name of the component to wrap
|
|
156
|
-
* @param hasProps - Whether the component accepts props
|
|
157
|
-
*/
|
|
158
|
-
export declare function generateRenderFunction(componentName: string, hasProps?: boolean): string;
|
|
159
|
-
//# sourceMappingURL=react.d.ts.map
|
package/dist/react.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAKL,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,aAAa,EACnB,MAAM,OAAO,CAAC;AAEf;;;;GAIG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC9D;;;;;;OAMG;IACH,MAAM,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;CACpE;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC5D,uDAAuD;IACvD,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD,6CAA6C;IAC7C,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,wCAAwC;IACxC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iEAAiE;IACjE,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/B,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,gCAAgC;IAChC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACxD,MAAM,EACN,KAAK,EACL,SAAS,EACT,KAAK,EACL,EAAE,EACF,QAAe,EACf,OAAO,EACP,SAAS,EACT,OAAO,GACR,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,YAAY,GAAG,IAAI,CA4D5C;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,6CAA6C;IAC7C,YAAY,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC/C,iDAAiD;IACjD,SAAS,EAAE,OAAO,CAAC;IACnB,8CAA8C;IAC9C,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,kDAAkD;IAClD,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAClD,qCAAqC;IACrC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7D,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EACjD,KAAK,CAAC,EAAE,CAAC,GACR,yBAAyB,CA8C3B;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,QAe1B,CAAC;AAET;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,EACrB,QAAQ,UAAO,GACd,MAAM,CAoBR"}
|
package/dist/react.js
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
// src/react.tsx
|
|
2
|
-
import {
|
|
3
|
-
useRef,
|
|
4
|
-
useEffect,
|
|
5
|
-
useCallback,
|
|
6
|
-
useState
|
|
7
|
-
} from "react";
|
|
8
|
-
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
9
|
-
function DynamicMount({
|
|
10
|
-
module,
|
|
11
|
-
props,
|
|
12
|
-
className,
|
|
13
|
-
style,
|
|
14
|
-
id,
|
|
15
|
-
fallback = null,
|
|
16
|
-
onMount,
|
|
17
|
-
onUnmount,
|
|
18
|
-
onError
|
|
19
|
-
}) {
|
|
20
|
-
const containerRef = useRef(null);
|
|
21
|
-
const cleanupRef = useRef(null);
|
|
22
|
-
const [error, setError] = useState(null);
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
if (!module || !containerRef.current)
|
|
25
|
-
return;
|
|
26
|
-
try {
|
|
27
|
-
if (cleanupRef.current) {
|
|
28
|
-
cleanupRef.current();
|
|
29
|
-
cleanupRef.current = null;
|
|
30
|
-
onUnmount?.();
|
|
31
|
-
}
|
|
32
|
-
setError(null);
|
|
33
|
-
const cleanup = module.render(containerRef.current, props);
|
|
34
|
-
cleanupRef.current = cleanup ?? null;
|
|
35
|
-
onMount?.();
|
|
36
|
-
} catch (err) {
|
|
37
|
-
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
38
|
-
setError(error2);
|
|
39
|
-
onError?.(error2);
|
|
40
|
-
}
|
|
41
|
-
return () => {
|
|
42
|
-
if (cleanupRef.current) {
|
|
43
|
-
cleanupRef.current();
|
|
44
|
-
cleanupRef.current = null;
|
|
45
|
-
onUnmount?.();
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
}, [module, props, onMount, onUnmount, onError]);
|
|
49
|
-
if (!module) {
|
|
50
|
-
return fallback;
|
|
51
|
-
}
|
|
52
|
-
if (error) {
|
|
53
|
-
return /* @__PURE__ */ jsxDEV("div", {
|
|
54
|
-
style: {
|
|
55
|
-
color: "red",
|
|
56
|
-
padding: "8px",
|
|
57
|
-
border: "1px solid red",
|
|
58
|
-
borderRadius: "4px"
|
|
59
|
-
},
|
|
60
|
-
children: [
|
|
61
|
-
"Failed to render dynamic component: ",
|
|
62
|
-
error.message
|
|
63
|
-
]
|
|
64
|
-
}, undefined, true, undefined, this);
|
|
65
|
-
}
|
|
66
|
-
return /* @__PURE__ */ jsxDEV("div", {
|
|
67
|
-
ref: containerRef,
|
|
68
|
-
className,
|
|
69
|
-
style,
|
|
70
|
-
id
|
|
71
|
-
}, undefined, false, undefined, this);
|
|
72
|
-
}
|
|
73
|
-
function useDynamicComponent(module, props) {
|
|
74
|
-
const containerRef = useRef(null);
|
|
75
|
-
const cleanupRef = useRef(null);
|
|
76
|
-
const propsRef = useRef(props);
|
|
77
|
-
const [isMounted, setIsMounted] = useState(false);
|
|
78
|
-
const [error, setError] = useState(null);
|
|
79
|
-
propsRef.current = props;
|
|
80
|
-
const unmount = useCallback(() => {
|
|
81
|
-
if (cleanupRef.current) {
|
|
82
|
-
cleanupRef.current();
|
|
83
|
-
cleanupRef.current = null;
|
|
84
|
-
}
|
|
85
|
-
setIsMounted(false);
|
|
86
|
-
}, []);
|
|
87
|
-
const update = useCallback((newProps) => {
|
|
88
|
-
if (!module || !containerRef.current)
|
|
89
|
-
return;
|
|
90
|
-
try {
|
|
91
|
-
unmount();
|
|
92
|
-
setError(null);
|
|
93
|
-
const cleanup = module.render(containerRef.current, newProps ?? propsRef.current);
|
|
94
|
-
cleanupRef.current = cleanup ?? null;
|
|
95
|
-
setIsMounted(true);
|
|
96
|
-
} catch (err) {
|
|
97
|
-
setError(err instanceof Error ? err : new Error(String(err)));
|
|
98
|
-
}
|
|
99
|
-
}, [module, unmount]);
|
|
100
|
-
useEffect(() => {
|
|
101
|
-
if (module && containerRef.current) {
|
|
102
|
-
update(props);
|
|
103
|
-
}
|
|
104
|
-
return unmount;
|
|
105
|
-
}, [module, props, update, unmount]);
|
|
106
|
-
return { containerRef, isMounted, error, update, unmount };
|
|
107
|
-
}
|
|
108
|
-
var REACT_RENDER_TEMPLATE = `
|
|
109
|
-
import React from "react";
|
|
110
|
-
import { createRoot } from "react-dom/client";
|
|
111
|
-
|
|
112
|
-
// Your component here
|
|
113
|
-
function MyComponent(props) {
|
|
114
|
-
return <div>Hello {props.name}</div>;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Export a render function that mounts using esm.sh's ReactDOM
|
|
118
|
-
export function render(container, props) {
|
|
119
|
-
const root = createRoot(container);
|
|
120
|
-
root.render(<MyComponent {...props} />);
|
|
121
|
-
return () => root.unmount();
|
|
122
|
-
}
|
|
123
|
-
`.trim();
|
|
124
|
-
function generateRenderFunction(componentName, hasProps = true) {
|
|
125
|
-
if (hasProps) {
|
|
126
|
-
return `
|
|
127
|
-
// Export a render function that mounts using esm.sh's ReactDOM
|
|
128
|
-
export function render(container: HTMLElement, props?: Parameters<typeof ${componentName}>[0]) {
|
|
129
|
-
const root = createRoot(container);
|
|
130
|
-
root.render(<${componentName} {...props} />);
|
|
131
|
-
return () => root.unmount();
|
|
132
|
-
}
|
|
133
|
-
`.trim();
|
|
134
|
-
}
|
|
135
|
-
return `
|
|
136
|
-
// Export a render function that mounts using esm.sh's ReactDOM
|
|
137
|
-
export function render(container: HTMLElement) {
|
|
138
|
-
const root = createRoot(container);
|
|
139
|
-
root.render(<${componentName} />);
|
|
140
|
-
return () => root.unmount();
|
|
141
|
-
}
|
|
142
|
-
`.trim();
|
|
143
|
-
}
|
|
144
|
-
export {
|
|
145
|
-
useDynamicComponent,
|
|
146
|
-
generateRenderFunction,
|
|
147
|
-
REACT_RENDER_TEMPLATE,
|
|
148
|
-
DynamicMount
|
|
149
|
-
};
|