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