@memberjunction/ng-react
Version:
Angular components for hosting React components in MemberJunction applications
214 lines • 7.97 kB
JavaScript
/**
* @fileoverview Angular adapter service that bridges the React runtime with Angular.
* Provides Angular-specific functionality for the platform-agnostic React runtime.
* @module @memberjunction/ng-react
*/
import { Injectable } from '@angular/core';
import { createReactRuntime } from '@memberjunction/react-runtime';
import { ScriptLoaderService } from './script-loader.service';
import { DEFAULT_STYLES } from '../default-styles';
import * as i0 from "@angular/core";
import * as i1 from "./script-loader.service";
/**
* Angular-specific adapter for the React runtime.
* Manages the integration between Angular services and the platform-agnostic React runtime.
*/
export class AngularAdapterService {
constructor(scriptLoader) {
this.scriptLoader = scriptLoader;
}
/**
* Initialize the React runtime with Angular-specific configuration
* @param config Optional library configuration
* @param additionalLibraries Optional additional libraries to merge
* @returns Promise resolving when runtime is ready
*/
async initialize(config, additionalLibraries) {
if (this.runtime) {
return; // Already initialized
}
// Load React ecosystem with optional additional libraries
const ecosystem = await this.scriptLoader.loadReactEcosystem(config, additionalLibraries);
// Create runtime context
this.runtimeContext = {
React: ecosystem.React,
ReactDOM: ecosystem.ReactDOM,
libraries: ecosystem.libraries,
utilities: {
// Add any Angular-specific utilities here
}
};
// Create the React runtime
this.runtime = createReactRuntime(ecosystem.Babel, {
compiler: {
cache: true,
maxCacheSize: 100
},
registry: {
maxComponents: 1000,
cleanupInterval: 60000,
useLRU: true,
enableNamespaces: true
}
});
}
/**
* Get the component compiler
* @returns Component compiler instance
*/
getCompiler() {
if (!this.runtime) {
throw new Error('React runtime not initialized. Call initialize() first.');
}
return this.runtime.compiler;
}
/**
* Get the component registry
* @returns Component registry instance
*/
getRegistry() {
if (!this.runtime) {
throw new Error('React runtime not initialized. Call initialize() first.');
}
return this.runtime.registry;
}
/**
* Get the component resolver
* @returns Component resolver instance
*/
getResolver() {
if (!this.runtime) {
throw new Error('React runtime not initialized. Call initialize() first.');
}
return this.runtime.resolver;
}
/**
* Get the runtime context
* @returns Runtime context with React and libraries
*/
getRuntimeContext() {
if (!this.runtimeContext) {
throw new Error('React runtime not initialized. Call initialize() first.');
}
return this.runtimeContext;
}
/**
* Compile a component with Angular-specific defaults
* @param options - Compilation options
* @returns Promise resolving to compilation result
*/
async compileComponent(options) {
// Validate options before initialization
if (!options) {
throw new Error('Angular adapter error: No compilation options provided.\n' +
'This usually means the component spec is null or undefined.\n' +
'Please check that:\n' +
'1. Your component data is loaded properly\n' +
'2. The component spec has "name" and "code" properties\n' +
'3. The component input is not undefined');
}
if (!options.componentName || options.componentName.trim() === '') {
throw new Error('Angular adapter error: Component name is missing or empty.\n' +
`Received options: ${JSON.stringify(options, null, 2)}\n` +
'Make sure your component spec includes a "name" property.');
}
if (!options.componentCode || options.componentCode.trim() === '') {
throw new Error(`Angular adapter error: Component code is missing or empty for component "${options.componentName}".\n` +
'Make sure your component spec includes a "code" property with the React component source.');
}
await this.initialize();
// Apply default styles if not provided
const optionsWithDefaults = {
...options,
styles: options.styles || DEFAULT_STYLES
};
return this.runtime.compiler.compile(optionsWithDefaults);
}
/**
* Register a component in the registry
* @param name - Component name
* @param component - Compiled component
* @param namespace - Component namespace
* @param version - Component version
* @returns Component metadata
*/
registerComponent(name, component, namespace = 'Global', version = 'v1') {
if (!this.runtime) {
throw new Error('React runtime not initialized. Call initialize() first.');
}
return this.runtime.registry.register(name, component, namespace, version);
}
/**
* Get a component from the registry
* @param name - Component name
* @param namespace - Component namespace
* @param version - Component version
* @returns Component if found
*/
getComponent(name, namespace = 'Global', version) {
if (!this.runtime) {
throw new Error('React runtime not initialized. Call initialize() first.');
}
return this.runtime.registry.get(name, namespace, version);
}
/**
* Check if runtime is initialized
* @returns true if initialized
*/
isInitialized() {
return !!this.runtime && !!this.runtimeContext;
}
/**
* Get runtime version
* @returns Runtime version string
*/
getVersion() {
return this.runtime?.version || 'unknown';
}
/**
* Clean up resources
*/
destroy() {
if (this.runtime) {
this.runtime.registry.destroy();
this.runtime = undefined;
this.runtimeContext = undefined;
}
}
/**
* Get Babel instance for direct use
* @returns Babel instance
*/
getBabel() {
return this.runtimeContext?.libraries?.Babel || window.Babel;
}
/**
* Transpile JSX code directly
* @param code - JSX code to transpile
* @param filename - Optional filename for better error messages
* @returns Transpiled JavaScript code
*/
transpileJSX(code, filename) {
const babel = this.getBabel();
if (!babel) {
throw new Error('Babel not loaded. Initialize the runtime first.');
}
try {
const result = babel.transform(code, {
presets: ['react'],
filename: filename || 'component.jsx'
});
return result.code;
}
catch (error) {
throw new Error(`Failed to transpile JSX: ${error.message}`);
}
}
static { this.ɵfac = function AngularAdapterService_Factory(t) { return new (t || AngularAdapterService)(i0.ɵɵinject(i1.ScriptLoaderService)); }; }
static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: AngularAdapterService, factory: AngularAdapterService.ɵfac, providedIn: 'root' }); }
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AngularAdapterService, [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], () => [{ type: i1.ScriptLoaderService }], null); })();
//# sourceMappingURL=angular-adapter.service.js.map