@memberjunction/react-runtime 2.70.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.
Files changed (58) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +3 -0
  3. package/README.md +224 -0
  4. package/dist/compiler/babel-config.d.ts +40 -0
  5. package/dist/compiler/babel-config.d.ts.map +1 -0
  6. package/dist/compiler/babel-config.js +52 -0
  7. package/dist/compiler/component-compiler.d.ts +22 -0
  8. package/dist/compiler/component-compiler.d.ts.map +1 -0
  9. package/dist/compiler/component-compiler.js +188 -0
  10. package/dist/compiler/index.d.ts +3 -0
  11. package/dist/compiler/index.d.ts.map +1 -0
  12. package/dist/compiler/index.js +13 -0
  13. package/dist/index.d.ts +41 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +95 -0
  16. package/dist/registry/component-registry.d.ts +32 -0
  17. package/dist/registry/component-registry.d.ts.map +1 -0
  18. package/dist/registry/component-registry.js +197 -0
  19. package/dist/registry/component-resolver.d.ts +29 -0
  20. package/dist/registry/component-resolver.d.ts.map +1 -0
  21. package/dist/registry/component-resolver.js +112 -0
  22. package/dist/registry/index.d.ts +3 -0
  23. package/dist/registry/index.d.ts.map +1 -0
  24. package/dist/registry/index.js +7 -0
  25. package/dist/runtime/component-hierarchy.d.ts +44 -0
  26. package/dist/runtime/component-hierarchy.d.ts.map +1 -0
  27. package/dist/runtime/component-hierarchy.js +162 -0
  28. package/dist/runtime/component-wrapper.d.ts +18 -0
  29. package/dist/runtime/component-wrapper.d.ts.map +1 -0
  30. package/dist/runtime/component-wrapper.js +108 -0
  31. package/dist/runtime/error-boundary.d.ts +6 -0
  32. package/dist/runtime/error-boundary.d.ts.map +1 -0
  33. package/dist/runtime/error-boundary.js +139 -0
  34. package/dist/runtime/index.d.ts +5 -0
  35. package/dist/runtime/index.d.ts.map +1 -0
  36. package/dist/runtime/index.js +31 -0
  37. package/dist/runtime/prop-builder.d.ts +16 -0
  38. package/dist/runtime/prop-builder.d.ts.map +1 -0
  39. package/dist/runtime/prop-builder.js +161 -0
  40. package/dist/types/index.d.ts +98 -0
  41. package/dist/types/index.d.ts.map +1 -0
  42. package/dist/types/index.js +2 -0
  43. package/package.json +36 -0
  44. package/src/compiler/babel-config.ts +97 -0
  45. package/src/compiler/component-compiler.ts +366 -0
  46. package/src/compiler/index.ts +15 -0
  47. package/src/index.ts +125 -0
  48. package/src/registry/component-registry.ts +379 -0
  49. package/src/registry/component-resolver.ts +275 -0
  50. package/src/registry/index.ts +7 -0
  51. package/src/runtime/component-hierarchy.ts +346 -0
  52. package/src/runtime/component-wrapper.ts +249 -0
  53. package/src/runtime/error-boundary.ts +242 -0
  54. package/src/runtime/index.ts +45 -0
  55. package/src/runtime/prop-builder.ts +290 -0
  56. package/src/types/index.ts +226 -0
  57. package/tsconfig.json +37 -0
  58. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,366 @@
1
+ /**
2
+ * @fileoverview Platform-agnostic React component compiler.
3
+ * Compiles React component source code into executable components using Babel.
4
+ * @module @memberjunction/react-runtime/compiler
5
+ */
6
+
7
+ import {
8
+ CompileOptions,
9
+ CompiledComponent,
10
+ CompilationResult,
11
+ CompilerConfig,
12
+ ComponentError,
13
+ RuntimeContext
14
+ } from '../types';
15
+
16
+ /**
17
+ * Default compiler configuration
18
+ */
19
+ const DEFAULT_COMPILER_CONFIG: CompilerConfig = {
20
+ babel: {
21
+ presets: ['react'],
22
+ plugins: []
23
+ },
24
+ minify: false,
25
+ sourceMaps: false,
26
+ cache: true,
27
+ maxCacheSize: 100
28
+ };
29
+
30
+ /**
31
+ * Platform-agnostic React component compiler.
32
+ * Transforms JSX/React component source code into executable JavaScript.
33
+ */
34
+ export class ComponentCompiler {
35
+ private config: CompilerConfig;
36
+ private compilationCache: Map<string, CompiledComponent>;
37
+ private babelInstance: any;
38
+
39
+ /**
40
+ * Creates a new ComponentCompiler instance
41
+ * @param config - Optional compiler configuration
42
+ */
43
+ constructor(config?: Partial<CompilerConfig>) {
44
+ this.config = { ...DEFAULT_COMPILER_CONFIG, ...config };
45
+ this.compilationCache = new Map();
46
+ }
47
+
48
+ /**
49
+ * Sets the Babel instance to use for compilation
50
+ * @param babel - The Babel standalone instance
51
+ */
52
+ setBabelInstance(babel: any): void {
53
+ this.babelInstance = babel;
54
+ }
55
+
56
+ /**
57
+ * Compiles a React component from source code
58
+ * @param options - Compilation options
59
+ * @returns Promise resolving to compilation result
60
+ */
61
+ async compile(options: CompileOptions): Promise<CompilationResult> {
62
+ const startTime = Date.now();
63
+
64
+ try {
65
+ // Check cache first if enabled
66
+ if (this.config.cache) {
67
+ const cached = this.getCachedComponent(options.componentName);
68
+ if (cached) {
69
+ return {
70
+ success: true,
71
+ component: cached,
72
+ duration: Date.now() - startTime
73
+ };
74
+ }
75
+ }
76
+
77
+ // Validate inputs
78
+ this.validateCompileOptions(options);
79
+
80
+ // Transpile the component code
81
+ const transpiledCode = this.transpileComponent(
82
+ options.componentCode,
83
+ options.componentName,
84
+ options
85
+ );
86
+
87
+ // Create the component factory
88
+ const componentFactory = this.createComponentFactory(
89
+ transpiledCode,
90
+ options.componentName
91
+ );
92
+
93
+ // Build the compiled component
94
+ const compiledComponent: CompiledComponent = {
95
+ component: componentFactory,
96
+ id: this.generateComponentId(options.componentName),
97
+ name: options.componentName,
98
+ compiledAt: new Date(),
99
+ warnings: []
100
+ };
101
+
102
+ // Cache if enabled
103
+ if (this.config.cache) {
104
+ this.cacheComponent(compiledComponent);
105
+ }
106
+
107
+ return {
108
+ success: true,
109
+ component: compiledComponent,
110
+ duration: Date.now() - startTime,
111
+ size: transpiledCode.length
112
+ };
113
+
114
+ } catch (error) {
115
+ return {
116
+ success: false,
117
+ error: this.createCompilationError(error, options.componentName),
118
+ duration: Date.now() - startTime
119
+ };
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Transpiles JSX/React code to JavaScript
125
+ * @param code - Source code to transpile
126
+ * @param componentName - Name of the component
127
+ * @param options - Compilation options
128
+ * @returns Transpiled JavaScript code
129
+ */
130
+ private transpileComponent(
131
+ code: string,
132
+ componentName: string,
133
+ options: CompileOptions
134
+ ): string {
135
+ if (!this.babelInstance) {
136
+ throw new Error('Babel instance not set. Call setBabelInstance() first.');
137
+ }
138
+
139
+ const wrappedCode = this.wrapComponentCode(code, componentName);
140
+
141
+ try {
142
+ const result = this.babelInstance.transform(wrappedCode, {
143
+ presets: options.babelPresets || this.config.babel.presets,
144
+ plugins: options.babelPlugins || this.config.babel.plugins,
145
+ filename: `${componentName}.jsx`,
146
+ sourceMaps: this.config.sourceMaps,
147
+ minified: this.config.minify
148
+ });
149
+
150
+ return result.code;
151
+ } catch (error: any) {
152
+ throw new Error(`Transpilation failed: ${error.message}`);
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Wraps component code in a factory function for execution
158
+ * @param componentCode - Raw component code
159
+ * @param componentName - Name of the component
160
+ * @returns Wrapped component code
161
+ */
162
+ private wrapComponentCode(componentCode: string, componentName: string): string {
163
+ return `
164
+ function createComponent(React, ReactDOM, useState, useEffect, useCallback, createStateUpdater, libraries, styles, console) {
165
+ ${componentCode}
166
+
167
+ // Ensure the component exists
168
+ if (typeof ${componentName} === 'undefined') {
169
+ throw new Error('Component "${componentName}" is not defined in the provided code');
170
+ }
171
+
172
+ // Return the component with utilities
173
+ return {
174
+ component: ${componentName},
175
+ print: function() {
176
+ if (typeof window !== 'undefined' && window.print) {
177
+ window.print();
178
+ }
179
+ },
180
+ refresh: function(data) {
181
+ // Refresh functionality is handled by the host environment
182
+ }
183
+ };
184
+ }
185
+ `;
186
+ }
187
+
188
+ /**
189
+ * Creates a component factory function from transpiled code
190
+ * @param transpiledCode - Transpiled JavaScript code
191
+ * @param componentName - Name of the component
192
+ * @returns Component factory function
193
+ */
194
+ private createComponentFactory(transpiledCode: string, componentName: string): Function {
195
+ try {
196
+ // Create the factory function
197
+ const factoryCreator = new Function(
198
+ 'React', 'ReactDOM', 'useState', 'useEffect', 'useCallback',
199
+ 'createStateUpdater', 'libraries', 'styles', 'console',
200
+ `${transpiledCode}; return createComponent;`
201
+ );
202
+
203
+ // Return a function that executes the factory with runtime context
204
+ return (context: RuntimeContext, styles: any = {}) => {
205
+ const { React, ReactDOM, libraries = {} } = context;
206
+
207
+ // Create state updater utility
208
+ const createStateUpdater = this.createStateUpdaterUtility();
209
+
210
+ // Execute the factory creator to get the createComponent function
211
+ const createComponentFn = factoryCreator(
212
+ React,
213
+ ReactDOM,
214
+ React.useState,
215
+ React.useEffect,
216
+ React.useCallback,
217
+ createStateUpdater,
218
+ libraries,
219
+ styles,
220
+ console
221
+ );
222
+
223
+ // Call createComponent to get the actual component
224
+ return createComponentFn(
225
+ React,
226
+ ReactDOM,
227
+ React.useState,
228
+ React.useEffect,
229
+ React.useCallback,
230
+ createStateUpdater,
231
+ libraries,
232
+ styles,
233
+ console
234
+ );
235
+ };
236
+ } catch (error: any) {
237
+ throw new Error(`Failed to create component factory: ${error.message}`);
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Creates the state updater utility function for nested components
243
+ * @returns State updater function
244
+ */
245
+ private createStateUpdaterUtility(): Function {
246
+ return (statePath: string, parentStateUpdater: Function) => {
247
+ return (componentStateUpdate: any) => {
248
+ if (!statePath) {
249
+ // Root component - pass through directly
250
+ parentStateUpdater(componentStateUpdate);
251
+ } else {
252
+ // Sub-component - bubble up with path context
253
+ const pathParts = statePath.split('.');
254
+ const componentKey = pathParts[pathParts.length - 1];
255
+
256
+ parentStateUpdater({
257
+ [componentKey]: componentStateUpdate
258
+ });
259
+ }
260
+ };
261
+ };
262
+ }
263
+
264
+ /**
265
+ * Validates compilation options
266
+ * @param options - Options to validate
267
+ * @throws Error if validation fails
268
+ */
269
+ private validateCompileOptions(options: CompileOptions): void {
270
+ if (!options.componentName) {
271
+ throw new Error('Component name is required');
272
+ }
273
+
274
+ if (!options.componentCode) {
275
+ throw new Error('Component code is required');
276
+ }
277
+
278
+ if (typeof options.componentCode !== 'string') {
279
+ throw new Error('Component code must be a string');
280
+ }
281
+
282
+ // Basic syntax check
283
+ if (!options.componentCode.includes(options.componentName)) {
284
+ throw new Error(`Component code must define a component named "${options.componentName}"`);
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Generates a unique component ID
290
+ * @param componentName - Name of the component
291
+ * @returns Unique component ID
292
+ */
293
+ private generateComponentId(componentName: string): string {
294
+ return `${componentName}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
295
+ }
296
+
297
+ /**
298
+ * Gets a cached component if available
299
+ * @param componentName - Name of the component
300
+ * @returns Cached component or undefined
301
+ */
302
+ private getCachedComponent(componentName: string): CompiledComponent | undefined {
303
+ // Simple cache lookup by name
304
+ // In production, might want to include code hash for cache key
305
+ for (const [key, component] of this.compilationCache) {
306
+ if (component.name === componentName) {
307
+ return component;
308
+ }
309
+ }
310
+ return undefined;
311
+ }
312
+
313
+ /**
314
+ * Caches a compiled component
315
+ * @param component - Component to cache
316
+ */
317
+ private cacheComponent(component: CompiledComponent): void {
318
+ // Enforce cache size limit
319
+ if (this.compilationCache.size >= this.config.maxCacheSize) {
320
+ // Remove oldest entry (first in map)
321
+ const firstKey = this.compilationCache.keys().next().value;
322
+ this.compilationCache.delete(firstKey);
323
+ }
324
+
325
+ this.compilationCache.set(component.id, component);
326
+ }
327
+
328
+ /**
329
+ * Creates a standardized compilation error
330
+ * @param error - Original error
331
+ * @param componentName - Name of the component
332
+ * @returns Formatted component error
333
+ */
334
+ private createCompilationError(error: any, componentName: string): ComponentError {
335
+ return {
336
+ message: error.message || 'Unknown compilation error',
337
+ stack: error.stack,
338
+ componentName,
339
+ phase: 'compilation',
340
+ details: error
341
+ };
342
+ }
343
+
344
+ /**
345
+ * Clears the compilation cache
346
+ */
347
+ clearCache(): void {
348
+ this.compilationCache.clear();
349
+ }
350
+
351
+ /**
352
+ * Gets current cache size
353
+ * @returns Number of cached components
354
+ */
355
+ getCacheSize(): number {
356
+ return this.compilationCache.size;
357
+ }
358
+
359
+ /**
360
+ * Updates compiler configuration
361
+ * @param config - New configuration options
362
+ */
363
+ updateConfig(config: Partial<CompilerConfig>): void {
364
+ this.config = { ...this.config, ...config };
365
+ }
366
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @fileoverview Compiler module exports
3
+ * @module @memberjunction/react-runtime/compiler
4
+ */
5
+
6
+ export { ComponentCompiler } from './component-compiler';
7
+ export {
8
+ DEFAULT_PRESETS,
9
+ DEFAULT_PLUGINS,
10
+ PRODUCTION_CONFIG,
11
+ DEVELOPMENT_CONFIG,
12
+ getBabelConfig,
13
+ validateBabelPresets,
14
+ getJSXConfig
15
+ } from './babel-config';
package/src/index.ts ADDED
@@ -0,0 +1,125 @@
1
+ /**
2
+ * @fileoverview Main entry point for the MemberJunction React Runtime.
3
+ * Exports all public APIs for platform-agnostic React component compilation and execution.
4
+ * @module @memberjunction/react-runtime
5
+ */
6
+
7
+ // Import necessary classes for createReactRuntime function
8
+ import { ComponentCompiler } from './compiler';
9
+ import { ComponentRegistry } from './registry';
10
+ import { ComponentResolver } from './registry';
11
+
12
+ // Export all types
13
+ export * from './types';
14
+
15
+ // Export compiler APIs
16
+ export { ComponentCompiler } from './compiler';
17
+ export {
18
+ DEFAULT_PRESETS,
19
+ DEFAULT_PLUGINS,
20
+ PRODUCTION_CONFIG,
21
+ DEVELOPMENT_CONFIG,
22
+ getBabelConfig,
23
+ validateBabelPresets,
24
+ getJSXConfig
25
+ } from './compiler';
26
+
27
+ // Export registry APIs
28
+ export { ComponentRegistry } from './registry';
29
+ export {
30
+ ComponentResolver,
31
+ ComponentSpec,
32
+ ResolvedComponents
33
+ } from './registry';
34
+
35
+ // Export runtime APIs
36
+ export {
37
+ createErrorBoundary,
38
+ withErrorBoundary,
39
+ formatComponentError,
40
+ createErrorLogger
41
+ } from './runtime';
42
+
43
+ export {
44
+ wrapComponent,
45
+ memoizeComponent,
46
+ lazyComponent,
47
+ injectProps,
48
+ conditionalComponent,
49
+ withErrorHandler,
50
+ portalComponent,
51
+ WrapperOptions
52
+ } from './runtime';
53
+
54
+ export {
55
+ buildComponentProps,
56
+ normalizeCallbacks,
57
+ normalizeStyles,
58
+ validateComponentProps,
59
+ mergeProps,
60
+ createPropsTransformer,
61
+ wrapCallbacksWithLogging,
62
+ extractPropPaths,
63
+ PropBuilderOptions
64
+ } from './runtime';
65
+
66
+ export {
67
+ ComponentHierarchyRegistrar,
68
+ registerComponentHierarchy,
69
+ validateComponentSpec,
70
+ flattenComponentHierarchy,
71
+ countComponentsInHierarchy,
72
+ HierarchyRegistrationResult,
73
+ ComponentRegistrationError,
74
+ HierarchyRegistrationOptions
75
+ } from './runtime';
76
+
77
+ // Version information
78
+ export const VERSION = '2.69.1';
79
+
80
+ // Default configurations
81
+ export const DEFAULT_CONFIGS = {
82
+ compiler: {
83
+ babel: {
84
+ presets: ['react'],
85
+ plugins: []
86
+ },
87
+ minify: false,
88
+ sourceMaps: false,
89
+ cache: true,
90
+ maxCacheSize: 100
91
+ },
92
+ registry: {
93
+ maxComponents: 1000,
94
+ cleanupInterval: 60000,
95
+ useLRU: true,
96
+ enableNamespaces: true
97
+ }
98
+ };
99
+
100
+ /**
101
+ * Creates a complete React runtime instance with all necessary components
102
+ * @param babelInstance - Babel standalone instance for compilation
103
+ * @param config - Optional configuration overrides
104
+ * @returns Object containing compiler, registry, and resolver instances
105
+ */
106
+ export function createReactRuntime(
107
+ babelInstance: any,
108
+ config?: {
109
+ compiler?: Partial<import('./types').CompilerConfig>;
110
+ registry?: Partial<import('./types').RegistryConfig>;
111
+ }
112
+ ) {
113
+ const compiler = new ComponentCompiler(config?.compiler);
114
+ compiler.setBabelInstance(babelInstance);
115
+
116
+ const registry = new ComponentRegistry(config?.registry);
117
+ const resolver = new ComponentResolver(registry);
118
+
119
+ return {
120
+ compiler,
121
+ registry,
122
+ resolver,
123
+ version: VERSION
124
+ };
125
+ }