@memberjunction/react-runtime 2.111.0 → 2.112.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/.turbo/turbo-build.log +10 -11
- package/CHANGELOG.md +6 -6
- package/dist/compiler/component-compiler.d.ts.map +1 -1
- package/dist/compiler/component-compiler.js +66 -56
- package/dist/compiler/component-compiler.js.map +1 -1
- package/dist/component-manager/component-manager.d.ts.map +1 -1
- package/dist/component-manager/component-manager.js +48 -42
- package/dist/component-manager/component-manager.js.map +1 -1
- package/dist/component-manager/types.d.ts +1 -1
- package/dist/component-manager/types.d.ts.map +1 -1
- package/dist/component-manager/types.js.map +1 -1
- package/dist/registry/component-registry-service.d.ts +1 -1
- package/dist/registry/component-registry-service.d.ts.map +1 -1
- package/dist/registry/component-registry-service.js +34 -34
- package/dist/registry/component-registry-service.js.map +1 -1
- package/dist/registry/component-resolver.d.ts +1 -1
- package/dist/registry/component-resolver.d.ts.map +1 -1
- package/dist/registry/component-resolver.js +10 -11
- package/dist/registry/component-resolver.js.map +1 -1
- package/dist/runtime/component-hierarchy.d.ts +1 -1
- package/dist/runtime/component-hierarchy.d.ts.map +1 -1
- package/dist/runtime/component-hierarchy.js +43 -43
- package/dist/runtime/component-hierarchy.js.map +1 -1
- package/dist/runtime/prop-builder.d.ts.map +1 -1
- package/dist/runtime/prop-builder.js +22 -24
- package/dist/runtime/prop-builder.js.map +1 -1
- package/dist/runtime.umd.js +494 -511
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utilities/library-registry.d.ts +1 -1
- package/dist/utilities/library-registry.d.ts.map +1 -1
- package/dist/utilities/library-registry.js +10 -10
- package/dist/utilities/library-registry.js.map +1 -1
- package/package.json +5 -6
- package/src/compiler/component-compiler.ts +186 -164
- package/src/component-manager/component-manager.ts +162 -216
- package/src/component-manager/types.ts +27 -27
- package/src/registry/component-registry-service.ts +190 -218
- package/src/registry/component-resolver.ts +87 -98
- package/src/runtime/component-hierarchy.ts +57 -94
- package/src/runtime/prop-builder.ts +28 -33
- package/src/types/index.ts +3 -4
- package/src/utilities/library-registry.ts +14 -19
|
@@ -4,15 +4,8 @@
|
|
|
4
4
|
* @module @memberjunction/react-runtime/compiler
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { UserInfo } from '@memberjunction/
|
|
8
|
-
import {
|
|
9
|
-
CompileOptions,
|
|
10
|
-
CompiledComponent,
|
|
11
|
-
CompilationResult,
|
|
12
|
-
CompilerConfig,
|
|
13
|
-
ComponentError,
|
|
14
|
-
RuntimeContext
|
|
15
|
-
} from '../types';
|
|
7
|
+
import { UserInfo } from '@memberjunction/global';
|
|
8
|
+
import { CompileOptions, CompiledComponent, CompilationResult, CompilerConfig, ComponentError, RuntimeContext } from '../types';
|
|
16
9
|
import { ComponentStyles, ComponentObject } from '@memberjunction/interactive-component-types';
|
|
17
10
|
import { LibraryRegistry } from '../utilities/library-registry';
|
|
18
11
|
import { LibraryLoader } from '../utilities/library-loader';
|
|
@@ -25,13 +18,13 @@ import { ComponentLibraryEntity } from '@memberjunction/core-entities';
|
|
|
25
18
|
const DEFAULT_COMPILER_CONFIG: CompilerConfig = {
|
|
26
19
|
babel: {
|
|
27
20
|
presets: ['react'],
|
|
28
|
-
plugins: []
|
|
21
|
+
plugins: [],
|
|
29
22
|
},
|
|
30
23
|
minify: false,
|
|
31
24
|
sourceMaps: false,
|
|
32
25
|
cache: true,
|
|
33
26
|
maxCacheSize: 100,
|
|
34
|
-
debug: false
|
|
27
|
+
debug: false,
|
|
35
28
|
};
|
|
36
29
|
|
|
37
30
|
/**
|
|
@@ -77,7 +70,7 @@ export class ComponentCompiler {
|
|
|
77
70
|
return {
|
|
78
71
|
success: true,
|
|
79
72
|
component: cached,
|
|
80
|
-
duration: Date.now() - startTime
|
|
73
|
+
duration: Date.now() - startTime,
|
|
81
74
|
};
|
|
82
75
|
}
|
|
83
76
|
}
|
|
@@ -89,19 +82,10 @@ export class ComponentCompiler {
|
|
|
89
82
|
const loadedLibraries = await this.loadRequiredLibraries(options.libraries!, options.allLibraries);
|
|
90
83
|
|
|
91
84
|
// Transpile the component code
|
|
92
|
-
const transpiledCode = this.transpileComponent(
|
|
93
|
-
options.componentCode,
|
|
94
|
-
options.componentName,
|
|
95
|
-
options
|
|
96
|
-
);
|
|
85
|
+
const transpiledCode = this.transpileComponent(options.componentCode, options.componentName, options);
|
|
97
86
|
|
|
98
87
|
// Create the component factory with loaded libraries
|
|
99
|
-
const componentFactory = this.createComponentFactory(
|
|
100
|
-
transpiledCode,
|
|
101
|
-
options.componentName,
|
|
102
|
-
loadedLibraries,
|
|
103
|
-
options
|
|
104
|
-
);
|
|
88
|
+
const componentFactory = this.createComponentFactory(transpiledCode, options.componentName, loadedLibraries, options);
|
|
105
89
|
|
|
106
90
|
// Build the compiled component
|
|
107
91
|
const compiledComponent: CompiledComponent = {
|
|
@@ -109,7 +93,7 @@ export class ComponentCompiler {
|
|
|
109
93
|
id: this.generateComponentId(options.componentName),
|
|
110
94
|
name: options.componentName,
|
|
111
95
|
compiledAt: new Date(),
|
|
112
|
-
warnings: []
|
|
96
|
+
warnings: [],
|
|
113
97
|
};
|
|
114
98
|
|
|
115
99
|
// Cache if enabled
|
|
@@ -122,14 +106,13 @@ export class ComponentCompiler {
|
|
|
122
106
|
component: compiledComponent,
|
|
123
107
|
duration: Date.now() - startTime,
|
|
124
108
|
size: transpiledCode.length,
|
|
125
|
-
loadedLibraries: loadedLibraries
|
|
109
|
+
loadedLibraries: loadedLibraries,
|
|
126
110
|
};
|
|
127
|
-
|
|
128
111
|
} catch (error) {
|
|
129
112
|
return {
|
|
130
113
|
success: false,
|
|
131
114
|
error: this.createCompilationError(error, options.componentName),
|
|
132
|
-
duration: Date.now() - startTime
|
|
115
|
+
duration: Date.now() - startTime,
|
|
133
116
|
};
|
|
134
117
|
}
|
|
135
118
|
}
|
|
@@ -141,11 +124,7 @@ export class ComponentCompiler {
|
|
|
141
124
|
* @param options - Compilation options
|
|
142
125
|
* @returns Transpiled JavaScript code
|
|
143
126
|
*/
|
|
144
|
-
private transpileComponent(
|
|
145
|
-
code: string,
|
|
146
|
-
componentName: string,
|
|
147
|
-
options: CompileOptions
|
|
148
|
-
): string {
|
|
127
|
+
private transpileComponent(code: string, componentName: string, options: CompileOptions): string {
|
|
149
128
|
if (!this.babelInstance) {
|
|
150
129
|
throw new Error('Babel instance not set. Call setBabelInstance() first.');
|
|
151
130
|
}
|
|
@@ -158,7 +137,7 @@ export class ComponentCompiler {
|
|
|
158
137
|
plugins: options.babelPlugins || this.config.babel.plugins,
|
|
159
138
|
filename: `${componentName}.jsx`,
|
|
160
139
|
sourceMaps: this.config.sourceMaps,
|
|
161
|
-
minified: this.config.minify
|
|
140
|
+
minified: this.config.minify,
|
|
162
141
|
});
|
|
163
142
|
|
|
164
143
|
return result.code;
|
|
@@ -178,22 +157,32 @@ export class ComponentCompiler {
|
|
|
178
157
|
// Core libraries that are passed as parameters to createComponent and should not be destructured
|
|
179
158
|
private readonly CORE_LIBRARIES = new Set(['React', 'ReactDOM']);
|
|
180
159
|
|
|
181
|
-
private wrapComponentCode(
|
|
160
|
+
private wrapComponentCode(
|
|
161
|
+
componentCode: string,
|
|
162
|
+
componentName: string,
|
|
163
|
+
libraries?: any[],
|
|
164
|
+
dependencies?: Array<{ name: string }>
|
|
165
|
+
): string {
|
|
182
166
|
const debug = this.config.debug;
|
|
183
167
|
// Generate library declarations if libraries are provided
|
|
184
168
|
// Skip core libraries as they're passed as parameters to createComponent
|
|
185
|
-
const libraryDeclarations =
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
169
|
+
const libraryDeclarations =
|
|
170
|
+
libraries && libraries.length > 0
|
|
171
|
+
? libraries
|
|
172
|
+
.filter((lib) => lib.globalVariable && !this.CORE_LIBRARIES.has(lib.globalVariable)) // Skip core libraries
|
|
173
|
+
.map((lib) => `const ${lib.globalVariable} = libraries['${lib.globalVariable}'];`)
|
|
174
|
+
.join('\n ')
|
|
175
|
+
: '';
|
|
176
|
+
const libraryLogChecks =
|
|
177
|
+
libraries && libraries.length > 0
|
|
178
|
+
? libraries
|
|
179
|
+
.filter((lib) => lib.globalVariable && !this.CORE_LIBRARIES.has(lib.globalVariable)) // Skip core libraries
|
|
180
|
+
.map(
|
|
181
|
+
(lib) =>
|
|
182
|
+
`\nif (!${lib.globalVariable}) { console.error('[React-Runtime-JS] Library "${lib.globalVariable}" is not defined'); } else { ${debug ? `console.log('[React-Runtime-JS] Library "${lib.globalVariable}" is defined');` : ''} }`
|
|
183
|
+
)
|
|
184
|
+
.join('\n ')
|
|
185
|
+
: '';
|
|
197
186
|
|
|
198
187
|
// Generate component declarations if dependencies are provided
|
|
199
188
|
// Filter out the component being compiled to avoid naming conflicts
|
|
@@ -201,7 +190,7 @@ export class ComponentCompiler {
|
|
|
201
190
|
const seenDependencies = new Set<string>();
|
|
202
191
|
const uniqueDependencies: Array<{ name: string; code?: string }> = [];
|
|
203
192
|
const duplicates: string[] = [];
|
|
204
|
-
|
|
193
|
+
|
|
205
194
|
if (dependencies && dependencies.length > 0) {
|
|
206
195
|
for (const dep of dependencies) {
|
|
207
196
|
if (dep.name === componentName) {
|
|
@@ -216,40 +205,58 @@ export class ComponentCompiler {
|
|
|
216
205
|
}
|
|
217
206
|
}
|
|
218
207
|
}
|
|
219
|
-
|
|
208
|
+
|
|
220
209
|
// Generate warning for duplicates
|
|
221
|
-
const duplicateWarnings =
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
210
|
+
const duplicateWarnings =
|
|
211
|
+
duplicates.length > 0
|
|
212
|
+
? duplicates
|
|
213
|
+
.map(
|
|
214
|
+
(name) =>
|
|
215
|
+
`console.warn('[React-Runtime-JS] WARNING: Component "${name}" is registered multiple times as a dependency. Using first occurrence only.');`
|
|
216
|
+
)
|
|
217
|
+
.join('\n ')
|
|
218
|
+
: '';
|
|
219
|
+
|
|
220
|
+
const componentDeclarations =
|
|
221
|
+
uniqueDependencies.length > 0
|
|
222
|
+
? uniqueDependencies
|
|
223
|
+
.map(
|
|
224
|
+
(dep) => `const ${dep.name}Raw = componentsOuter['${dep.name}'];
|
|
225
|
+
${
|
|
226
|
+
debug
|
|
227
|
+
? `console.log('[React-Runtime-JS] Extracting ${dep.name}:');
|
|
231
228
|
console.log(' - Raw value type:', typeof ${dep.name}Raw);
|
|
232
229
|
console.log(' - Raw value:', ${dep.name}Raw);
|
|
233
230
|
if (${dep.name}Raw && typeof ${dep.name}Raw === 'object') {
|
|
234
231
|
console.log(' - Has .component property:', 'component' in ${dep.name}Raw);
|
|
235
232
|
console.log(' - .component type:', typeof ${dep.name}Raw.component);
|
|
236
|
-
}`
|
|
233
|
+
}`
|
|
234
|
+
: ''
|
|
235
|
+
}
|
|
237
236
|
const ${dep.name} = ${dep.name}Raw?.component || ${dep.name}Raw;
|
|
238
|
-
${
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
237
|
+
${
|
|
238
|
+
debug
|
|
239
|
+
? `console.log(' - Final ${dep.name} type:', typeof ${dep.name});
|
|
240
|
+
console.log(' - Final ${dep.name} is function:', typeof ${dep.name} === 'function');`
|
|
241
|
+
: ''
|
|
242
|
+
}`
|
|
243
|
+
)
|
|
244
|
+
.join('\n ')
|
|
245
|
+
: '';
|
|
246
|
+
|
|
247
|
+
const componentLogChecks =
|
|
248
|
+
uniqueDependencies.length > 0
|
|
249
|
+
? uniqueDependencies
|
|
250
|
+
.map(
|
|
251
|
+
(dep) =>
|
|
252
|
+
`if (!${dep.name}) { console.error('[React-Runtime-JS] Dependency "${dep.name}" is not defined'); } else { ${debug ? `console.log('[React-Runtime-JS] Dependency "${dep.name}" is defined');` : ''} }`
|
|
253
|
+
)
|
|
254
|
+
.join('\n ')
|
|
255
|
+
: '';
|
|
249
256
|
|
|
250
257
|
const wrappedCode = `
|
|
251
258
|
function createComponent(
|
|
252
|
-
React, ReactDOM,
|
|
259
|
+
React, ReactDOM,
|
|
253
260
|
useState, useEffect, useCallback, useMemo, useRef, useContext, useReducer, useLayoutEffect,
|
|
254
261
|
libraries, styles, console, components,
|
|
255
262
|
unwrapLibraryComponent, unwrapLibraryComponents, unwrapAllLibraryComponents
|
|
@@ -263,10 +270,10 @@ export class ComponentCompiler {
|
|
|
263
270
|
const unwrapComponent = unwrapLibraryComponent;
|
|
264
271
|
const unwrapComponents = unwrapLibraryComponents;
|
|
265
272
|
const unwrapAllComponents = unwrapAllLibraryComponents;
|
|
266
|
-
|
|
273
|
+
|
|
267
274
|
// Code for ${componentName}
|
|
268
275
|
${componentCode}
|
|
269
|
-
|
|
276
|
+
|
|
270
277
|
// Ensure the component exists
|
|
271
278
|
if (typeof ${componentName} === 'undefined') {
|
|
272
279
|
throw new Error('Component "${componentName}" is not defined in the provided code');
|
|
@@ -274,7 +281,7 @@ export class ComponentCompiler {
|
|
|
274
281
|
else {
|
|
275
282
|
${debug ? `console.log('[React-Runtime-JS] Component "${componentName}" is defined');` : ''}
|
|
276
283
|
}
|
|
277
|
-
|
|
284
|
+
|
|
278
285
|
// Store the component in a variable so we don't lose it
|
|
279
286
|
const UserComponent = ${componentName};
|
|
280
287
|
|
|
@@ -283,9 +290,11 @@ export class ComponentCompiler {
|
|
|
283
290
|
const ActualComponent = (typeof UserComponent === 'object' && UserComponent !== null && 'component' in UserComponent)
|
|
284
291
|
? UserComponent.component
|
|
285
292
|
: UserComponent;
|
|
286
|
-
|
|
293
|
+
|
|
287
294
|
// Debug logging to understand what we're getting
|
|
288
|
-
${
|
|
295
|
+
${
|
|
296
|
+
debug
|
|
297
|
+
? `
|
|
289
298
|
console.log('[React-Runtime-JS]Component ${componentName} type:', typeof UserComponent);
|
|
290
299
|
if (typeof UserComponent === 'object' && UserComponent !== null) {
|
|
291
300
|
console.log('[React-Runtime-JS]Component ${componentName} keys:', Object.keys(UserComponent));
|
|
@@ -293,8 +302,10 @@ export class ComponentCompiler {
|
|
|
293
302
|
if ('component' in UserComponent) {
|
|
294
303
|
console.log('[React-Runtime-JS]Component ${componentName}.component type:', typeof UserComponent.component);
|
|
295
304
|
}
|
|
296
|
-
}`
|
|
297
|
-
|
|
305
|
+
}`
|
|
306
|
+
: ''
|
|
307
|
+
}
|
|
308
|
+
|
|
298
309
|
// Validate that we have a function (React component)
|
|
299
310
|
if (typeof ActualComponent !== 'function') {
|
|
300
311
|
console.error('[React-Runtime-JS] Invalid component type for ${componentName}:', typeof ActualComponent);
|
|
@@ -311,7 +322,9 @@ export class ComponentCompiler {
|
|
|
311
322
|
if (!utilitiesOuter) {
|
|
312
323
|
utilitiesOuter = props?.utilities;
|
|
313
324
|
}
|
|
314
|
-
${
|
|
325
|
+
${
|
|
326
|
+
debug
|
|
327
|
+
? `
|
|
315
328
|
console.log('[React-Runtime-JS] DestructureWrapperUserComponent for ${componentName}:');
|
|
316
329
|
console.log(' - Props:', props);
|
|
317
330
|
console.log(' - componentsOuter type:', typeof componentsOuter);
|
|
@@ -329,42 +342,44 @@ export class ComponentCompiler {
|
|
|
329
342
|
}
|
|
330
343
|
console.log(' - styles:', styles);
|
|
331
344
|
console.log(' - utilities:', utilitiesOuter);
|
|
332
|
-
console.log(' - libraries:', libraries);`
|
|
345
|
+
console.log(' - libraries:', libraries);`
|
|
346
|
+
: ''
|
|
347
|
+
}
|
|
333
348
|
${duplicateWarnings ? '// Duplicate dependency warnings\n ' + duplicateWarnings + '\n ' : ''}
|
|
334
349
|
${libraryDeclarations ? '// Destructure Libraries\n' + libraryDeclarations + '\n ' : ''}
|
|
335
350
|
${componentDeclarations ? '// Destructure Dependencies\n' + componentDeclarations + '\n ' : ''}
|
|
336
351
|
${libraryLogChecks}
|
|
337
|
-
${componentLogChecks}
|
|
352
|
+
${componentLogChecks}
|
|
338
353
|
|
|
339
354
|
const newProps = {
|
|
340
355
|
...props,
|
|
341
356
|
components: componentsOuter,
|
|
342
|
-
utilities: utilitiesOuter
|
|
357
|
+
utilities: utilitiesOuter
|
|
343
358
|
}
|
|
344
359
|
return ActualComponent(newProps);
|
|
345
360
|
};
|
|
346
|
-
|
|
361
|
+
|
|
347
362
|
// Create a fresh method registry for each factory call
|
|
348
363
|
const methodRegistry = new Map();
|
|
349
|
-
|
|
364
|
+
|
|
350
365
|
// Create a wrapper component that provides RegisterMethod in callbacks
|
|
351
366
|
const ComponentWithMethodRegistry = (props) => {
|
|
352
367
|
// Register methods on mount
|
|
353
368
|
React.useEffect(() => {
|
|
354
369
|
// Clear previous methods
|
|
355
370
|
methodRegistry.clear();
|
|
356
|
-
|
|
371
|
+
|
|
357
372
|
// Provide RegisterMethod callback if callbacks exist
|
|
358
373
|
if (props.callbacks && typeof props.callbacks.RegisterMethod === 'function') {
|
|
359
374
|
// Component can now register its methods
|
|
360
375
|
// This will be called from within the component
|
|
361
376
|
}
|
|
362
377
|
}, [props.callbacks]);
|
|
363
|
-
|
|
378
|
+
|
|
364
379
|
// Create enhanced callbacks with RegisterMethod
|
|
365
380
|
const enhancedCallbacks = React.useMemo(() => {
|
|
366
381
|
if (!props.callbacks) return {};
|
|
367
|
-
|
|
382
|
+
|
|
368
383
|
return {
|
|
369
384
|
...props.callbacks,
|
|
370
385
|
RegisterMethod: (methodName, handler) => {
|
|
@@ -374,36 +389,36 @@ export class ComponentCompiler {
|
|
|
374
389
|
}
|
|
375
390
|
};
|
|
376
391
|
}, [props.callbacks]);
|
|
377
|
-
|
|
392
|
+
|
|
378
393
|
// Render the original component with enhanced callbacks
|
|
379
394
|
return React.createElement(DestructureWrapperUserComponent, {
|
|
380
395
|
...props,
|
|
381
396
|
callbacks: enhancedCallbacks
|
|
382
397
|
});
|
|
383
398
|
};
|
|
384
|
-
|
|
399
|
+
|
|
385
400
|
ComponentWithMethodRegistry.displayName = '${componentName}WithMethods';
|
|
386
|
-
|
|
401
|
+
|
|
387
402
|
// Return the component object with method access
|
|
388
403
|
return {
|
|
389
404
|
component: ComponentWithMethodRegistry,
|
|
390
|
-
|
|
391
|
-
print: function() {
|
|
405
|
+
|
|
406
|
+
print: function() {
|
|
392
407
|
const printMethod = methodRegistry.get('print');
|
|
393
408
|
if (printMethod) {
|
|
394
409
|
printMethod();
|
|
395
410
|
} else if (typeof window !== 'undefined' && window.print) {
|
|
396
|
-
window.print();
|
|
411
|
+
window.print();
|
|
397
412
|
}
|
|
398
413
|
},
|
|
399
|
-
refresh: function(data) {
|
|
414
|
+
refresh: function(data) {
|
|
400
415
|
const refreshMethod = methodRegistry.get('refresh');
|
|
401
416
|
if (refreshMethod) {
|
|
402
417
|
refreshMethod(data);
|
|
403
418
|
}
|
|
404
419
|
// Refresh functionality is handled by the host environment
|
|
405
420
|
},
|
|
406
|
-
|
|
421
|
+
|
|
407
422
|
// Standard method accessors with type safety
|
|
408
423
|
getCurrentDataState: function() {
|
|
409
424
|
const method = methodRegistry.get('getCurrentDataState');
|
|
@@ -433,7 +448,7 @@ export class ComponentCompiler {
|
|
|
433
448
|
const method = methodRegistry.get('focus');
|
|
434
449
|
if (method) method(target);
|
|
435
450
|
},
|
|
436
|
-
|
|
451
|
+
|
|
437
452
|
// Generic method invoker for custom methods
|
|
438
453
|
invokeMethod: function(methodName, ...args) {
|
|
439
454
|
const method = methodRegistry.get(methodName);
|
|
@@ -443,7 +458,7 @@ export class ComponentCompiler {
|
|
|
443
458
|
console.warn(\`[React-Runtime-JS] Method '\${methodName}' is not registered on component ${componentName}\`);
|
|
444
459
|
return undefined;
|
|
445
460
|
},
|
|
446
|
-
|
|
461
|
+
|
|
447
462
|
// Check if a method exists
|
|
448
463
|
hasMethod: function(methodName) {
|
|
449
464
|
return methodRegistry.has(methodName);
|
|
@@ -463,14 +478,14 @@ export class ComponentCompiler {
|
|
|
463
478
|
*/
|
|
464
479
|
private async loadRequiredLibraries(libraries: any[], componentLibraries: ComponentLibraryEntity[]): Promise<Map<string, any>> {
|
|
465
480
|
const loadedLibraries = new Map<string, any>();
|
|
466
|
-
|
|
481
|
+
|
|
467
482
|
if (this.config.debug) {
|
|
468
483
|
console.log('🔍 loadRequiredLibraries called with:', {
|
|
469
484
|
librariesCount: libraries?.length || 0,
|
|
470
|
-
libraries: libraries?.map(l => ({ name: l.name, version: l.version, globalVariable: l.globalVariable }))
|
|
485
|
+
libraries: libraries?.map((l) => ({ name: l.name, version: l.version, globalVariable: l.globalVariable })),
|
|
471
486
|
});
|
|
472
487
|
}
|
|
473
|
-
|
|
488
|
+
|
|
474
489
|
if (!libraries || libraries.length === 0) {
|
|
475
490
|
if (this.config.debug) {
|
|
476
491
|
console.log('📚 No libraries to load, returning empty map');
|
|
@@ -492,25 +507,25 @@ export class ComponentCompiler {
|
|
|
492
507
|
}
|
|
493
508
|
|
|
494
509
|
// Filter out React, ReactDOM, and invalid library entries
|
|
495
|
-
const filteredLibraries = libraries.filter(lib => {
|
|
510
|
+
const filteredLibraries = libraries.filter((lib) => {
|
|
496
511
|
// Check if library object is valid
|
|
497
512
|
if (!lib || typeof lib !== 'object' || !lib.name) {
|
|
498
513
|
console.warn(`⚠️ Invalid library entry detected (missing name):`, lib);
|
|
499
514
|
return false;
|
|
500
515
|
}
|
|
501
|
-
|
|
516
|
+
|
|
502
517
|
// Filter out entries with 'unknown' name or missing globalVariable
|
|
503
518
|
if (lib.name === 'unknown' || lib.name === 'null' || lib.name === 'undefined') {
|
|
504
519
|
console.warn(`⚠️ Filtering out invalid library with name '${lib.name}':`, lib);
|
|
505
520
|
return false;
|
|
506
521
|
}
|
|
507
|
-
|
|
522
|
+
|
|
508
523
|
// Check for missing or invalid globalVariable
|
|
509
524
|
if (!lib.globalVariable || lib.globalVariable === 'undefined' || lib.globalVariable === 'null') {
|
|
510
525
|
console.warn(`⚠️ Filtering out library '${lib.name}' with invalid globalVariable:`, lib.globalVariable);
|
|
511
526
|
return false;
|
|
512
527
|
}
|
|
513
|
-
|
|
528
|
+
|
|
514
529
|
const libNameLower = lib.name.toLowerCase();
|
|
515
530
|
if (libNameLower === 'react' || libNameLower === 'reactdom') {
|
|
516
531
|
console.warn(`⚠️ Library '${lib.name}' is automatically loaded by the React runtime and should not be requested separately`);
|
|
@@ -518,16 +533,14 @@ export class ComponentCompiler {
|
|
|
518
533
|
}
|
|
519
534
|
return true;
|
|
520
535
|
});
|
|
521
|
-
|
|
536
|
+
|
|
522
537
|
// Extract library names from the filtered libraries (with extra safety)
|
|
523
|
-
const libraryNames = filteredLibraries
|
|
524
|
-
|
|
525
|
-
.filter(name => name && typeof name === 'string');
|
|
526
|
-
|
|
538
|
+
const libraryNames = filteredLibraries.map((lib) => lib.name).filter((name) => name && typeof name === 'string');
|
|
539
|
+
|
|
527
540
|
if (this.config.debug) {
|
|
528
541
|
console.log('📦 Using dependency-aware loading for libraries:', libraryNames);
|
|
529
542
|
}
|
|
530
|
-
|
|
543
|
+
|
|
531
544
|
// If all libraries were filtered out, return empty map
|
|
532
545
|
if (filteredLibraries.length === 0) {
|
|
533
546
|
if (this.config.debug) {
|
|
@@ -538,12 +551,9 @@ export class ComponentCompiler {
|
|
|
538
551
|
|
|
539
552
|
try {
|
|
540
553
|
// Use the new dependency-aware loading
|
|
541
|
-
const loadedLibraryMap = await LibraryLoader.loadLibrariesWithDependencies(
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
'component-compiler',
|
|
545
|
-
{ debug: this.config.debug }
|
|
546
|
-
);
|
|
554
|
+
const loadedLibraryMap = await LibraryLoader.loadLibrariesWithDependencies(libraryNames, componentLibraries, 'component-compiler', {
|
|
555
|
+
debug: this.config.debug,
|
|
556
|
+
});
|
|
547
557
|
|
|
548
558
|
// Map the results to match the expected format
|
|
549
559
|
// We need to map from library name to global variable
|
|
@@ -557,7 +567,7 @@ export class ComponentCompiler {
|
|
|
557
567
|
|
|
558
568
|
// Get the loaded library from the map
|
|
559
569
|
const loadedValue = loadedLibraryMap.get(lib.name);
|
|
560
|
-
|
|
570
|
+
|
|
561
571
|
if (loadedValue) {
|
|
562
572
|
// Store by global variable name for component access
|
|
563
573
|
loadedLibraries.set(lib.globalVariable, loadedValue);
|
|
@@ -580,12 +590,12 @@ export class ComponentCompiler {
|
|
|
580
590
|
}
|
|
581
591
|
} catch (error: any) {
|
|
582
592
|
console.error('Failed to load libraries with dependencies:', error);
|
|
583
|
-
|
|
593
|
+
|
|
584
594
|
// Fallback to old loading method if dependency resolution fails
|
|
585
595
|
if (this.config.debug) {
|
|
586
596
|
console.warn('⚠️ Falling back to non-dependency-aware loading due to error');
|
|
587
597
|
}
|
|
588
|
-
|
|
598
|
+
|
|
589
599
|
// Load each library independently (old method)
|
|
590
600
|
for (const lib of libraries) {
|
|
591
601
|
if ((window as any)[lib.globalVariable]) {
|
|
@@ -596,7 +606,7 @@ export class ComponentCompiler {
|
|
|
596
606
|
if (libraryDef) {
|
|
597
607
|
const resolvedVersion = LibraryRegistry.resolveVersion(lib.name, lib.version);
|
|
598
608
|
const cdnUrl = LibraryRegistry.getCdnUrl(lib.name, resolvedVersion);
|
|
599
|
-
|
|
609
|
+
|
|
600
610
|
if (cdnUrl) {
|
|
601
611
|
await this.loadScript(cdnUrl, lib.globalVariable);
|
|
602
612
|
const libraryValue = (window as any)[lib.globalVariable];
|
|
@@ -608,12 +618,12 @@ export class ComponentCompiler {
|
|
|
608
618
|
}
|
|
609
619
|
}
|
|
610
620
|
}
|
|
611
|
-
|
|
621
|
+
|
|
612
622
|
if (this.config.debug) {
|
|
613
623
|
console.log(`✅ All libraries loaded successfully. Total: ${loadedLibraries.size}`);
|
|
614
624
|
console.log('📚 Loaded libraries map:', Array.from(loadedLibraries.keys()));
|
|
615
625
|
}
|
|
616
|
-
|
|
626
|
+
|
|
617
627
|
return loadedLibraries;
|
|
618
628
|
}
|
|
619
629
|
|
|
@@ -623,7 +633,7 @@ export class ComponentCompiler {
|
|
|
623
633
|
* @returns Promise that resolves when all stylesheets are loaded
|
|
624
634
|
*/
|
|
625
635
|
private async loadStyles(urls: string[]): Promise<void> {
|
|
626
|
-
const loadPromises = urls.map(url => {
|
|
636
|
+
const loadPromises = urls.map((url) => {
|
|
627
637
|
return new Promise<void>((resolve) => {
|
|
628
638
|
// Check if stylesheet already exists
|
|
629
639
|
const existingLink = document.querySelector(`link[href="${url}"]`);
|
|
@@ -636,7 +646,7 @@ export class ComponentCompiler {
|
|
|
636
646
|
const link = document.createElement('link');
|
|
637
647
|
link.rel = 'stylesheet';
|
|
638
648
|
link.href = url;
|
|
639
|
-
|
|
649
|
+
|
|
640
650
|
// CSS load events are not reliable cross-browser, so resolve immediately
|
|
641
651
|
// The CSS will load asynchronously but won't block component rendering
|
|
642
652
|
document.head.appendChild(link);
|
|
@@ -679,13 +689,13 @@ export class ComponentCompiler {
|
|
|
679
689
|
const script = document.createElement('script');
|
|
680
690
|
script.src = url;
|
|
681
691
|
script.async = true;
|
|
682
|
-
|
|
692
|
+
|
|
683
693
|
script.onload = () => {
|
|
684
694
|
// More robust checking with multiple attempts
|
|
685
695
|
let attempts = 0;
|
|
686
696
|
const maxAttempts = 20; // 2 seconds total
|
|
687
697
|
const checkInterval = 100; // Check every 100ms
|
|
688
|
-
|
|
698
|
+
|
|
689
699
|
const checkGlobal = () => {
|
|
690
700
|
if ((window as any)[globalName]) {
|
|
691
701
|
if (this.config.debug) {
|
|
@@ -697,7 +707,7 @@ export class ComponentCompiler {
|
|
|
697
707
|
if (this.config.debug) {
|
|
698
708
|
console.error(` ❌ ${globalName} not found after ${attempts * checkInterval}ms`);
|
|
699
709
|
// Only log matching property names, not the entire window object
|
|
700
|
-
const matchingKeys = Object.keys(window).filter(k => k.toLowerCase().includes(globalName.toLowerCase()));
|
|
710
|
+
const matchingKeys = Object.keys(window).filter((k) => k.toLowerCase().includes(globalName.toLowerCase()));
|
|
701
711
|
console.log(` ℹ️ Matching window properties: ${matchingKeys.join(', ') || 'none'}`);
|
|
702
712
|
}
|
|
703
713
|
reject(new Error(`${globalName} not found after loading script from ${url}`));
|
|
@@ -706,15 +716,15 @@ export class ComponentCompiler {
|
|
|
706
716
|
setTimeout(checkGlobal, checkInterval);
|
|
707
717
|
}
|
|
708
718
|
};
|
|
709
|
-
|
|
719
|
+
|
|
710
720
|
// Start checking immediately (don't wait 100ms first)
|
|
711
721
|
checkGlobal();
|
|
712
722
|
};
|
|
713
|
-
|
|
723
|
+
|
|
714
724
|
script.onerror = () => {
|
|
715
725
|
reject(new Error(`Failed to load script: ${url}`));
|
|
716
726
|
};
|
|
717
|
-
|
|
727
|
+
|
|
718
728
|
document.head.appendChild(script);
|
|
719
729
|
});
|
|
720
730
|
}
|
|
@@ -728,7 +738,7 @@ export class ComponentCompiler {
|
|
|
728
738
|
* @returns Component factory function
|
|
729
739
|
*/
|
|
730
740
|
private createComponentFactory(
|
|
731
|
-
transpiledCode: string,
|
|
741
|
+
transpiledCode: string,
|
|
732
742
|
componentName: string,
|
|
733
743
|
loadedLibraries: Map<string, any>,
|
|
734
744
|
options: CompileOptions
|
|
@@ -736,17 +746,30 @@ export class ComponentCompiler {
|
|
|
736
746
|
try {
|
|
737
747
|
// Create the factory function with all React hooks and utility functions
|
|
738
748
|
const factoryCreator = new Function(
|
|
739
|
-
'React',
|
|
740
|
-
'
|
|
741
|
-
'
|
|
742
|
-
'
|
|
749
|
+
'React',
|
|
750
|
+
'ReactDOM',
|
|
751
|
+
'useState',
|
|
752
|
+
'useEffect',
|
|
753
|
+
'useCallback',
|
|
754
|
+
'useMemo',
|
|
755
|
+
'useRef',
|
|
756
|
+
'useContext',
|
|
757
|
+
'useReducer',
|
|
758
|
+
'useLayoutEffect',
|
|
759
|
+
'libraries',
|
|
760
|
+
'styles',
|
|
761
|
+
'console',
|
|
762
|
+
'components',
|
|
763
|
+
'unwrapLibraryComponent',
|
|
764
|
+
'unwrapLibraryComponents',
|
|
765
|
+
'unwrapAllLibraryComponents',
|
|
743
766
|
`${transpiledCode}; return createComponent;`
|
|
744
767
|
);
|
|
745
768
|
|
|
746
769
|
// Return a function that executes the factory with runtime context
|
|
747
770
|
return (context: RuntimeContext, styles: any = {}, components: Record<string, any> = {}) => {
|
|
748
771
|
const { React, ReactDOM, libraries = {} } = context;
|
|
749
|
-
|
|
772
|
+
|
|
750
773
|
// Diagnostic: Check if React is null when creating component
|
|
751
774
|
if (!React) {
|
|
752
775
|
console.error('🔴 CRITICAL: React is NULL in createComponentFactory!');
|
|
@@ -754,7 +777,7 @@ export class ComponentCompiler {
|
|
|
754
777
|
console.error('Context keys:', Object.keys(context));
|
|
755
778
|
throw new Error('React is null in runtime context when creating component factory');
|
|
756
779
|
}
|
|
757
|
-
|
|
780
|
+
|
|
758
781
|
// Additional diagnostic for React hooks
|
|
759
782
|
if (!React.useState || !React.useEffect) {
|
|
760
783
|
console.error('🔴 CRITICAL: React hooks are missing!');
|
|
@@ -762,18 +785,16 @@ export class ComponentCompiler {
|
|
|
762
785
|
console.error('useState:', typeof React?.useState);
|
|
763
786
|
console.error('useEffect:', typeof React?.useEffect);
|
|
764
787
|
}
|
|
765
|
-
|
|
788
|
+
|
|
766
789
|
// Merge loaded libraries with context libraries
|
|
767
790
|
// IMPORTANT: Only include libraries that are NOT dependency-only
|
|
768
791
|
// We need to filter based on the libraries array from options
|
|
769
792
|
const mergedLibraries = { ...libraries };
|
|
770
|
-
|
|
793
|
+
|
|
771
794
|
// Only add libraries that are explicitly requested in the component
|
|
772
795
|
// This prevents dependency-only libraries from being accessible
|
|
773
|
-
const specLibraryNames = new Set(
|
|
774
|
-
|
|
775
|
-
);
|
|
776
|
-
|
|
796
|
+
const specLibraryNames = new Set((options.libraries || []).map((lib: any) => lib.globalVariable).filter(Boolean));
|
|
797
|
+
|
|
777
798
|
loadedLibraries.forEach((value, key) => {
|
|
778
799
|
// Only add if this library is in the spec (not just a dependency)
|
|
779
800
|
if (specLibraryNames.has(key)) {
|
|
@@ -785,7 +806,8 @@ export class ComponentCompiler {
|
|
|
785
806
|
|
|
786
807
|
// Create bound versions of unwrap functions with debug flag
|
|
787
808
|
const boundUnwrapLibraryComponent = (lib: any, name: string) => unwrapLibraryComponent(lib, name, this.config.debug);
|
|
788
|
-
const boundUnwrapLibraryComponents = (lib: any, ...names: string[]) =>
|
|
809
|
+
const boundUnwrapLibraryComponents = (lib: any, ...names: string[]) =>
|
|
810
|
+
unwrapLibraryComponents(lib, ...names, this.config.debug as any);
|
|
789
811
|
const boundUnwrapAllLibraryComponents = (lib: any) => unwrapAllLibraryComponents(lib, this.config.debug);
|
|
790
812
|
|
|
791
813
|
// Execute the factory creator to get the createComponent function
|
|
@@ -851,7 +873,6 @@ export class ComponentCompiler {
|
|
|
851
873
|
}
|
|
852
874
|
}
|
|
853
875
|
|
|
854
|
-
|
|
855
876
|
/**
|
|
856
877
|
* Validates compilation options
|
|
857
878
|
* @param options - Options to validate
|
|
@@ -862,8 +883,8 @@ export class ComponentCompiler {
|
|
|
862
883
|
if (!options) {
|
|
863
884
|
throw new Error(
|
|
864
885
|
'Component compilation failed: No options provided.\n' +
|
|
865
|
-
|
|
866
|
-
|
|
886
|
+
'Expected an object with componentName and componentCode properties.\n' +
|
|
887
|
+
'Example: { componentName: "MyComponent", componentCode: "function MyComponent() { ... }" }'
|
|
867
888
|
);
|
|
868
889
|
}
|
|
869
890
|
|
|
@@ -872,9 +893,9 @@ export class ComponentCompiler {
|
|
|
872
893
|
const providedKeys = Object.keys(options).join(', ');
|
|
873
894
|
throw new Error(
|
|
874
895
|
'Component compilation failed: Component name is required.\n' +
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
896
|
+
`Received options with keys: [${providedKeys}]\n` +
|
|
897
|
+
'Please ensure your component spec includes a "name" property.\n' +
|
|
898
|
+
'Example: { name: "MyComponent", code: "..." }'
|
|
878
899
|
);
|
|
879
900
|
}
|
|
880
901
|
|
|
@@ -882,8 +903,8 @@ export class ComponentCompiler {
|
|
|
882
903
|
if (!options.componentCode) {
|
|
883
904
|
throw new Error(
|
|
884
905
|
`Component compilation failed: Component code is required for "${options.componentName}".\n` +
|
|
885
|
-
|
|
886
|
-
|
|
906
|
+
'Please ensure your component spec includes a "code" property with the component source code.\n' +
|
|
907
|
+
'Example: { name: "MyComponent", code: "function MyComponent() { return <div>Hello</div>; }" }'
|
|
887
908
|
);
|
|
888
909
|
}
|
|
889
910
|
|
|
@@ -892,9 +913,9 @@ export class ComponentCompiler {
|
|
|
892
913
|
const actualType = typeof options.componentCode;
|
|
893
914
|
throw new Error(
|
|
894
915
|
`Component compilation failed: Component code must be a string for "${options.componentName}".\n` +
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
916
|
+
`Received type: ${actualType}\n` +
|
|
917
|
+
`Received value: ${JSON.stringify(options.componentCode).substring(0, 100)}...\n` +
|
|
918
|
+
'Please ensure the code property contains a string of JavaScript/JSX code.'
|
|
898
919
|
);
|
|
899
920
|
}
|
|
900
921
|
|
|
@@ -902,7 +923,7 @@ export class ComponentCompiler {
|
|
|
902
923
|
if (options.componentCode.trim().length === 0) {
|
|
903
924
|
throw new Error(
|
|
904
925
|
`Component compilation failed: Component code is empty for "${options.componentName}".\n` +
|
|
905
|
-
|
|
926
|
+
'The code property must contain valid JavaScript/JSX code defining a React component.'
|
|
906
927
|
);
|
|
907
928
|
}
|
|
908
929
|
|
|
@@ -910,9 +931,11 @@ export class ComponentCompiler {
|
|
|
910
931
|
if (!options.componentCode.includes(options.componentName)) {
|
|
911
932
|
throw new Error(
|
|
912
933
|
`Component compilation failed: Component code must define a component named "${options.componentName}".\n` +
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
934
|
+
'The function/component name in the code must match the componentName property.\n' +
|
|
935
|
+
`Expected to find: function ${options.componentName}(...) or const ${options.componentName} = ...\n` +
|
|
936
|
+
'Code preview: ' +
|
|
937
|
+
options.componentCode.substring(0, 200) +
|
|
938
|
+
'...'
|
|
916
939
|
);
|
|
917
940
|
}
|
|
918
941
|
}
|
|
@@ -949,7 +972,7 @@ export class ComponentCompiler {
|
|
|
949
972
|
let hash = 0;
|
|
950
973
|
for (let i = 0; i < code.length; i++) {
|
|
951
974
|
const char = code.charCodeAt(i);
|
|
952
|
-
hash = (
|
|
975
|
+
hash = (hash << 5) - hash + char;
|
|
953
976
|
hash = hash & hash; // Convert to 32-bit integer
|
|
954
977
|
}
|
|
955
978
|
return `${componentName}_${hash.toString(36)}`;
|
|
@@ -965,8 +988,7 @@ export class ComponentCompiler {
|
|
|
965
988
|
if (this.compilationCache.size >= this.config.maxCacheSize) {
|
|
966
989
|
// Remove oldest entry (first in map)
|
|
967
990
|
const firstKey = this.compilationCache.keys().next().value;
|
|
968
|
-
if (firstKey)
|
|
969
|
-
this.compilationCache.delete(firstKey);
|
|
991
|
+
if (firstKey) this.compilationCache.delete(firstKey);
|
|
970
992
|
}
|
|
971
993
|
|
|
972
994
|
const cacheKey = this.createCacheKey(component.name, code);
|
|
@@ -985,7 +1007,7 @@ export class ComponentCompiler {
|
|
|
985
1007
|
stack: error.stack,
|
|
986
1008
|
componentName,
|
|
987
1009
|
phase: 'compilation',
|
|
988
|
-
details: error
|
|
1010
|
+
details: error,
|
|
989
1011
|
};
|
|
990
1012
|
}
|
|
991
1013
|
|
|
@@ -1011,4 +1033,4 @@ export class ComponentCompiler {
|
|
|
1011
1033
|
updateConfig(config: Partial<CompilerConfig>): void {
|
|
1012
1034
|
this.config = { ...this.config, ...config };
|
|
1013
1035
|
}
|
|
1014
|
-
}
|
|
1036
|
+
}
|