UNPKG

@dotglitch/ngx-lazy-loader

Version:

A hackable lazy loader for Angular components

662 lines (652 loc) 31.6 kB
import * as i0 from '@angular/core'; import { InjectionToken, Injectable, Inject, EventEmitter, isDevMode, ViewContainerRef, Component, Optional, ViewChild, Input, Output, NgModule } from '@angular/core'; import { __awaiter } from 'tslib'; import { NgIf, NgComponentOutlet, NgTemplateOutlet } from '@angular/common'; import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { debounceTime } from 'rxjs'; import * as i2 from '@angular/cdk/dialog'; var ComponentResolveStrategy; (function (ComponentResolveStrategy) { /** * Match the fist component we find * (best used for standalone components) * @default */ ComponentResolveStrategy[ComponentResolveStrategy["PickFirst"] = 0] = "PickFirst"; /** * Perform an Exact ID to Classname of the Component * case sensitive, zero tolerance. */ ComponentResolveStrategy[ComponentResolveStrategy["MatchIdToClassName"] = 1] = "MatchIdToClassName"; /** * Perform a fuzzy ID to classname match * case insensitive, mutes symbols * ignores "Component" and "Module" postfixes on class * names */ ComponentResolveStrategy[ComponentResolveStrategy["FuzzyIdClassName"] = 2] = "FuzzyIdClassName"; /** * Use a user-provided component match function */ ComponentResolveStrategy[ComponentResolveStrategy["Custom"] = 3] = "Custom"; })(ComponentResolveStrategy || (ComponentResolveStrategy = {})); /** * Convert a string `fooBAR baz_160054''"1]"` into a slug: `foobar-baz-1600541` */ const stringToSlug = (text) => (text || '') .trim() .toLowerCase() .replace(/[\-_+ ]/g, '-') .replace(/[^a-z0-9\-]/g, ''); const Logger = (context, contextColor, textColor = "#03a9f4") => ({ log: (message, ...args) => { console.log(`%c[${context}] %c${message}`, 'color: ' + contextColor, 'color: ' + textColor, ...args); }, warn: (message, ...args) => { console.warn(`%c[${context}] %c${message}`, 'color: ' + contextColor, 'color: ' + textColor, ...args); }, err: (message, ...args) => { console.error(`%c[${context}] %c${message}`, 'color: ' + contextColor, 'color: ' + textColor, ...args); }, error: (message, ...args) => { console.error(`%c[${context}] %c${message}`, 'color: ' + contextColor, 'color: ' + textColor, ...args); } }); // Monkey-patch the type of these symbols. const $id = Symbol("id"); const $group = Symbol("group"); const NGX_LAZY_LOADER_CONFIG = new InjectionToken('config'); class NgxLazyLoaderService { get err() { return NgxLazyLoaderService.config.logger.err; } get log() { return NgxLazyLoaderService.config.logger.log; } get warn() { return NgxLazyLoaderService.config.logger.warn; } constructor(config = {}) { // Ensure this is singleton and works regardless of special instancing requirements. NgxLazyLoaderService.configure(config); } static configure(config) { var _a; const { log, warn, err } = Logger("ngx-lazy-loader", "#009688"); this.config = Object.assign({ componentResolveStrategy: ComponentResolveStrategy.PickFirst, logger: { log, warn, err } }, config); (_a = config.entries) === null || _a === void 0 ? void 0 : _a.forEach(e => this.addComponentToRegistry(e)); // If a custom resolution strategy is provided but no resolution function is passed, // we throw an error if (this.config.componentResolveStrategy == ComponentResolveStrategy.Custom && !this.config.customResolver) { throw new Error("Cannot initialize. Configuration specifies a custom resolve matcher but none was provided"); } if (this.config.loaderDistractorComponent && this.config.loaderDistractorTemplate) throw new Error("Cannot have both a Component and Template for Distractor view."); if (this.config.errorComponent && this.config.errorTemplate) throw new Error("Cannot have both a Component and Template for Error view."); if (this.config.notFoundComponent && this.config.notFoundTemplate) throw new Error("Cannot have both a Component and Template for NotFound view."); } static addComponentToRegistry(registration) { var _a; if (!registration) throw new Error("Cannot add <undefined> component into registry."); // Clone the object into our repository and transfer the id into a standardized slug format const id = stringToSlug((_a = registration.id) !== null && _a !== void 0 ? _a : Date.now().toString()); // purge non-basic ASCII chars const group = registration.group || "default"; registration[$id] = id; registration[$group] = id; if (!this.registry[group]) this.registry[group] = []; // Check if we already have a registration for the component // if (this.registry[group] && typeof this.registry[group]['load'] == "function") { // // Warn the developer that the state is problematic // this.config.logger.warn( // `A previous entry already exists for ${id}! The old registration will be overridden.` + // `Please ensure you use groups if you intend to have duplicate component ids. ` + // `If this was intentional, first remove the old component from the registry before adding a new instance` // ); // // If we're in dev mode, break the loader surface // if (isDevMode()) // return; // } this.registry[group].push(registration); } /** * Register an Angular component * @param id identifier that is used to resolve the component * @param group * @param component Angular Component Class constructor */ registerComponent(args) { if (this.isComponentRegistered(args.id, args.group)) { this.log(`Will not re-register component '${args.id}' in group '${args.group || 'default'}' `); return; } NgxLazyLoaderService.addComponentToRegistry({ id: stringToSlug(args.id), matcher: args.matcher, group: stringToSlug(args.group || "default"), load: args.load || (() => args.component) }); } /** * * @param id * @param group */ unregisterComponent(id, group = "default") { const _id = stringToSlug(id); const _group = stringToSlug(group); if (!this.resolveRegistrationEntry(id, group)) throw new Error("Cannot unregister component ${}! Component is not present in registry"); // TODO: handle clearing running instances delete NgxLazyLoaderService.registry[_group][_id]; } /** * Get the registration entry for a component. * Returns null if component is not in the registry. */ resolveRegistrationEntry(value, group = "default") { const _id = stringToSlug(value); const _group = stringToSlug(group); const targetGroup = (NgxLazyLoaderService.registry[_group] || []); let items = targetGroup.filter(t => { if (!t) return false; // No matcher, check id if (!t.matcher) return t[$id] == _id; // Matcher is regex if (t.matcher instanceof RegExp) return t.matcher.test(_id) || t.matcher.test(value); // Matcher is string => regex if (typeof t.matcher == 'string') { const rx = new RegExp(t.matcher, 'ui'); return rx.test(_id) || rx.test(value); } // Matcher is array if (Array.isArray(t.matcher)) { return !!t.matcher.find(e => stringToSlug(e) == _id); } // Custom matcher function if (typeof t.matcher == "function") return t.matcher(_id); return false; }); if (items.length > 1) { this.warn("Resolved multiple components for the provided `[component]` binding. This may cause UI conflicts."); } if (items.length == 0) { return null; } const out = items[0]; if (out.matcher instanceof RegExp) { const result = _id.match(out.matcher) || value.match(out.matcher); return { entry: out, matchGroups: result === null || result === void 0 ? void 0 : result.groups }; } return { entry: out }; } /** * Check if a component is currently registered * Can be used to validate regex matchers and aliases. */ isComponentRegistered(value, group = "default") { return !!this.resolveRegistrationEntry(value, group); } /** * * @param bundle * @returns The component `Object` if a component was resolved, `null` if no component was found * `false` if the specified strategy was an invalid selection */ resolveComponent(id, group, modules) { switch (NgxLazyLoaderService.config.componentResolveStrategy) { case ComponentResolveStrategy.PickFirst: { return modules[0]; } // Exact id -> classname match case ComponentResolveStrategy.MatchIdToClassName: { const matches = modules .filter(k => k.name == id); if (matches.length == 0) return null; return matches[0]; } // Fuzzy id -> classname match case ComponentResolveStrategy.FuzzyIdClassName: { const _id = id.replace(/[^a-z0-9_\-]/ig, ''); if (_id.length == 0) { NgxLazyLoaderService.config.logger.err("Fuzzy classname matching stripped all symbols from the ID specified!"); return false; } const rx = new RegExp(`^${id}(component|module)?$`, "i"); const matches = modules .filter(mod => { let kid = mod.name.replace(/[^a-z0-9_\-]/ig, ''); return rx.test(kid); }); if (matches.length > 1) { NgxLazyLoaderService.config.logger.err("Fuzzy classname matching resolved multiple targets!"); return false; } if (matches.length == 0) { NgxLazyLoaderService.config.logger.err("Fuzzy classname matching resolved no targets!"); return null; } return matches[0]; } case ComponentResolveStrategy.Custom: { return NgxLazyLoaderService.config.customResolver(modules); } default: { return false; } } } } // A proxied registry that mutates reference keys NgxLazyLoaderService.registry = {}; /** @nocollapse */ NgxLazyLoaderService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.0", ngImport: i0, type: NgxLazyLoaderService, deps: [{ token: NGX_LAZY_LOADER_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ NgxLazyLoaderService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.0", ngImport: i0, type: NgxLazyLoaderService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.0", ngImport: i0, type: NgxLazyLoaderService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Inject, args: [NGX_LAZY_LOADER_CONFIG] }] }]; } }); class NgxLazyLoaderComponent { /** * The id of the component that will be lazy loaded */ set id(data) { const id = stringToSlug(data); // Check if there is a change to the loaded component's id // if it's updated, we destroy and rehydrate the entire container if (this.initialized && this._id != id) { this._id = id; this.ngAfterViewInit(); } else { this._id = id; } } ; set group(data) { const group = stringToSlug(data); if (typeof group != "string" || !group) return; // If the group was updated, retry to bootstrap something into the container. if (this.initialized && this._group != group) { this._group = group; this.ngAfterViewInit(); return; } this._group = group; } get group() { return this._group; } /** * A map of inputs to bind to the child. * Supports change detection. (May fail on deep JSON changes) * * ```html * <lazy-loader component="MyLazyComponent" * [inputs]="{ * prop1: true, * prop2: false, * complex: { * a: true, * b: 0 * } * }" * > * </lazy-loader> * ``` */ set inputs(data) { if (data == undefined) return; let previous = this._inputs; this._inputs = data; if (data == undefined) console.trace(data); if (this.targetComponentFactory) { const { inputs } = this.targetComponentFactory.ɵcmp; const currentKeys = Object.keys(inputs); const oldKeys = Object.keys(previous).filter(key => currentKeys.includes(key)); const newKeys = Object.keys(data).filter(key => currentKeys.includes(key)); const removed = oldKeys.filter(key => !newKeys.includes(key)); // ? perhaps set to null or undefined instead removed.forEach(k => this.targetComponentInstance[k] = null); this.bindInputs(); } } /** * A map of outputs to bind from the child. * Should support change detection. * ```html * <lazy-loader component="MyLazyComponent" * [outputs]="{ * prop3: onOutputFire * }" * > * </lazy-loader> * ``` */ set outputs(data) { let previous = this._outputs; this._outputs = data; if (this.targetComponentFactory) { const { inputs } = this.targetComponentFactory.ɵcmp; const currentKeys = Object.keys(inputs); const removed = Object.keys(previous).filter(key => !currentKeys.includes(key)); removed.forEach(k => { var _a; // Unsubscribe from observable (_a = this.outputSubscriptions[k]) === null || _a === void 0 ? void 0 : _a.unsubscribe(); delete this.targetComponentInstance[k]; }); this.bindOutputs(); } } constructor(service, viewContainerRef, dialog, dialogArguments) { this.service = service; this.viewContainerRef = viewContainerRef; this.dialog = dialog; this.dialogArguments = dialogArguments; this._group = "default"; this.outputSubscriptions = {}; /** * Emits errors encountered when loading components */ this.componentLoadError = new EventEmitter(); /** * Emits when the component is fully constructed * and had it's inputs and outputs bound * > before `OnInit` * * Returns the active class instance of the lazy-loaded component */ this.componentLoaded = new EventEmitter(); // Force 500ms delay before revealing the spinner this.loaderEmitter = new EventEmitter(); this.clearLoader$ = this.loaderEmitter.pipe(debounceTime(300)); this.showLoader = true; // whether we render the DOM for the spinner this.isClearingLoader = false; // should the spinner start fading out this.initialized = false; this.config = NgxLazyLoaderService.config; this.err = NgxLazyLoaderService.config.logger.err; this.warn = NgxLazyLoaderService.config.logger.warn; this.log = NgxLazyLoaderService.config.logger.log; // First, check for dialog arguments if (this.dialogArguments) { this.inputs = this.dialogArguments.inputs || this.dialogArguments.data; this.outputs = this.dialogArguments.outputs; this.id = this.dialogArguments.id; this.group = this.dialogArguments.group; } this.loaderSub = this.clearLoader$.subscribe(() => { this.showLoader = false; }); } ngAfterViewInit() { return __awaiter(this, void 0, void 0, function* () { this.ngOnDestroy(false); this.isClearingLoader = false; this.showLoader = true; this.initialized = true; if (!this._id) { this.warn("No component was specified!"); return this.loadDefault(); } try { const _entry = this.service.resolveRegistrationEntry(this._id, this._group); if (!_entry || !_entry.entry) { this.err(`Failed to find Component '${this._id}' in group '${this._group}' in registry!`); return this.loadDefault(); } const { entry, matchGroups } = _entry; this._matchGroups = matchGroups; // Download the "module" (the standalone component) const bundle = this.targetModule = yield entry.load(); // Check if there is some corruption on the bundle. if (!bundle || typeof bundle != 'object' || bundle['__esModule'] !== true || bundle.toString() != "[object Module]") { this.err(`Failed to load component/module for '${this._id}'! Parsed resource is invalid.`); return this.loadError(); } const modules = Object.keys(bundle) .map(k => { const entry = bundle[k]; // Strictly check for exported modules or standalone components if (typeof entry == "function" && typeof entry["ɵfac"] == "function") return entry; return null; }) .filter(e => e != null) .filter(entry => { entry['_isModule'] = !!entry['ɵmod']; // module entry['_isComponent'] = !!entry['ɵcmp']; // component return (entry['_isModule'] || entry['_isComponent']); }); if (modules.length == 0) { this.err(`Component/Module loaded for '${this._id}' has no exported components or modules!`); return this.loadError(); } const component = this.targetComponentFactory = this.service.resolveComponent(this._id, "default", modules); if (!component) { this.err(`Component '${this._id}' is invalid or corrupted!`); return this.loadError(); } // Bootstrap the component into the container const componentRef = this.targetComponentContainerRef = this.targetContainer.createComponent(component); this.targetRef = this.targetContainer.insert(this.targetComponentContainerRef.hostView); const instance = this.targetComponentInstance = componentRef['instance']; this.bindInputs(); this.bindOutputs(); this.componentLoaded.next(instance); this.instance = instance; // Look for an observable called isLoading$ that will make us show/hide // the same distractor that is used on basic loading const isLoading$ = instance['ngxShowDistractor$']; if (isLoading$ && typeof isLoading$.subscribe == "function") { this.distractorSubscription = isLoading$.subscribe(loading => { if (!loading) { this.isClearingLoader = true; this.loaderEmitter.emit(); } else { this.showLoader = true; this.isClearingLoader = false; } }); } else { this.isClearingLoader = true; } const name = Object.keys(bundle)[0]; this.log(`Loaded '${name}'`); this.loaderEmitter.emit(); return componentRef; } catch (ex) { if (isDevMode()) { console.warn("Component " + this._id + " threw an error on mount!"); console.warn("This will cause you to see a 404 panel."); console.error(ex); } // Network errors throw a toast and return an error component if (ex && !isDevMode()) { console.error("Uncaught error when loading component"); throw ex; } return this.loadDefault(); } }); } ngOnDestroy(clearAll = true) { var _a, _b, _c, _d, _e; // unsubscribe from all subscriptions Object.entries(this.outputSubscriptions).forEach(([key, sub]) => { sub.unsubscribe(); }); this.outputSubscriptions = {}; // Clear all things if (clearAll) { (_a = this.loaderSub) === null || _a === void 0 ? void 0 : _a.unsubscribe(); } (_b = this.distractorSubscription) === null || _b === void 0 ? void 0 : _b.unsubscribe(); // Clear target container (_c = this.targetRef) === null || _c === void 0 ? void 0 : _c.destroy(); (_d = this.targetComponentContainerRef) === null || _d === void 0 ? void 0 : _d.destroy(); (_e = this.targetContainer) === null || _e === void 0 ? void 0 : _e.clear(); // Wipe the rest of the state clean this.targetRef = null; this.targetComponentContainerRef = null; } /** * Bind the input values to the child component. */ bindInputs() { if (!this._inputs || !this.targetComponentInstance) return; // Merge match groups if (typeof this._matchGroups == "object") { Object.entries(this._matchGroups).forEach(([key, val]) => { if (typeof this._inputs[key] == 'undefined') this._inputs[key] = val; }); } // forward-bind inputs const { inputs } = this.targetComponentFactory.ɵcmp; // Returns a list of entries that need to be set // This makes it so that unnecessary setters are not invoked. const updated = Object.entries(inputs).filter(([parentKey, childKey]) => { return this.targetComponentInstance[childKey] != this._inputs[parentKey]; }); updated.forEach(([parentKey, childKey]) => { if (this._inputs.hasOwnProperty(parentKey)) this.targetComponentInstance[childKey] = this._inputs[parentKey]; }); } /** * Bind the output handlers to the loaded child component */ bindOutputs() { if (!this._outputs || !this.targetComponentInstance) return; const { outputs } = this.targetComponentFactory.ɵcmp; // Get a list of unregistered outputs const newOutputs = Object.entries(outputs).filter(([parentKey, childKey]) => { return !this.outputSubscriptions[parentKey]; }); // Reverse bind via subscription newOutputs.forEach(([parentKey, childKey]) => { if (this._outputs.hasOwnProperty(parentKey)) { const target = this.targetComponentInstance[childKey]; const outputs = this._outputs; // Angular folks, stop making this so difficult. const ctx = this.viewContainerRef['_hostLView'][8]; const sub = target.subscribe(outputs[parentKey].bind(ctx)); // Subscription this.outputSubscriptions[parentKey] = sub; } }); } /** * Load the "Default" component (404) screen normally. * This is shown when the component id isn't in the * registry or otherwise doesn't match * * This */ loadDefault() { if (this.config.notFoundComponent) this.targetContainer.createComponent(this.config.notFoundComponent); this.showLoader = false; } /** * Load the "Error" component. * This is shown when we are able to resolve the component * in the registry, but have some issue boostrapping the * component into the viewContainer */ loadError() { if (this.config.errorComponent) this.targetContainer.createComponent(this.config.errorComponent); this.showLoader = false; } } /** @nocollapse */ NgxLazyLoaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.0", ngImport: i0, type: NgxLazyLoaderComponent, deps: [{ token: NgxLazyLoaderService }, { token: i0.ViewContainerRef, optional: true }, { token: i2.DialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ NgxLazyLoaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.0", type: NgxLazyLoaderComponent, isStandalone: true, selector: "ngx-lazy-loader", inputs: { id: ["component", "id"], group: "group", inputs: "inputs", outputs: "outputs" }, outputs: { componentLoadError: "componentLoadError", componentLoaded: "componentLoaded" }, viewQueries: [{ propertyName: "targetContainer", first: true, predicate: ["content"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "<ng-container #content></ng-container>\n\n<div class=\"ngx-lazy-loader-distractor\" [class.destroying]=\"isClearingLoader\">\n <ng-container *ngIf=\"config.loaderDistractorComponent\" [ngComponentOutlet]=\"config.loaderDistractorComponent\"></ng-container>\n <ng-container *ngIf=\"config.loaderDistractorTemplate\" [ngTemplateOutlet]=\"config.loaderDistractorTemplate\" [ngTemplateOutletContext]=\"{ '$implicit': inputs }\"></ng-container>\n</div>\n", styles: [":host{display:contents;contain:content;z-index:1;position:relative}.ngx-lazy-loader-distractor{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;background-color:var(--background-color, #212121);opacity:1;transition:opacity .3s ease;z-index:999999}.ngx-lazy-loader-distractor.destroying{opacity:0;pointer-events:none}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.0", ngImport: i0, type: NgxLazyLoaderComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-lazy-loader', imports: [NgIf, NgComponentOutlet, NgTemplateOutlet], standalone: true, template: "<ng-container #content></ng-container>\n\n<div class=\"ngx-lazy-loader-distractor\" [class.destroying]=\"isClearingLoader\">\n <ng-container *ngIf=\"config.loaderDistractorComponent\" [ngComponentOutlet]=\"config.loaderDistractorComponent\"></ng-container>\n <ng-container *ngIf=\"config.loaderDistractorTemplate\" [ngTemplateOutlet]=\"config.loaderDistractorTemplate\" [ngTemplateOutletContext]=\"{ '$implicit': inputs }\"></ng-container>\n</div>\n", styles: [":host{display:contents;contain:content;z-index:1;position:relative}.ngx-lazy-loader-distractor{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;background-color:var(--background-color, #212121);opacity:1;transition:opacity .3s ease;z-index:999999}.ngx-lazy-loader-distractor.destroying{opacity:0;pointer-events:none}\n"] }] }], ctorParameters: function () { return [{ type: NgxLazyLoaderService }, { type: i0.ViewContainerRef, decorators: [{ type: Optional }] }, { type: i2.DialogRef, decorators: [{ type: Optional }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [MAT_DIALOG_DATA] }] }]; }, propDecorators: { targetContainer: [{ type: ViewChild, args: ["content", { read: ViewContainerRef }] }], id: [{ type: Input, args: ["component"] }], group: [{ type: Input, args: ["group"] }], inputs: [{ type: Input, args: ["inputs"] }], outputs: [{ type: Input, args: ["outputs"] }], componentLoadError: [{ type: Output }], componentLoaded: [{ type: Output }] } }); class NgxLazyLoaderModule { static forRoot(config) { return ({ ngModule: NgxLazyLoaderModule, providers: [ NgxLazyLoaderService, { provide: NGX_LAZY_LOADER_CONFIG, useValue: config } ] }); } } /** @nocollapse */ NgxLazyLoaderModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.0", ngImport: i0, type: NgxLazyLoaderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); /** @nocollapse */ NgxLazyLoaderModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.0", ngImport: i0, type: NgxLazyLoaderModule, imports: [NgxLazyLoaderComponent], exports: [NgxLazyLoaderComponent] }); /** @nocollapse */ NgxLazyLoaderModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.0", ngImport: i0, type: NgxLazyLoaderModule, imports: [NgxLazyLoaderComponent] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.0", ngImport: i0, type: NgxLazyLoaderModule, decorators: [{ type: NgModule, args: [{ imports: [NgxLazyLoaderComponent], exports: [NgxLazyLoaderComponent] }] }] }); /* * Public API Surface of ngx-lazy-loader */ /** * Generated bundle index. Do not edit. */ export { ComponentResolveStrategy, NGX_LAZY_LOADER_CONFIG, NgxLazyLoaderComponent, NgxLazyLoaderModule, NgxLazyLoaderService }; //# sourceMappingURL=dotglitch-ngx-lazy-loader.mjs.map