Skip to content

Commit

Permalink
feat(🎁 ComponentRegistry): Added a completely new ComponentRegistry
Browse files Browse the repository at this point in the history
  • Loading branch information
artalat committed Sep 14, 2018
1 parent 729d9f9 commit 65da844
Show file tree
Hide file tree
Showing 16 changed files with 5,439 additions and 15 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@

## 🏆 Features

- 🔌 Plugins
- 🎣 Lifecycle events and hooks
- 🎁 Dynamic Components
- 📔 Logging API
86 changes: 78 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,23 @@
"node": ">=8.9"
},
"dependencies": {
"lodash.flowright": "^3.5.0",
"lodash.isfunction": "^3.0.9",
"lodash.isnil": "^4.0.0",
"lodash.kebabcase": "^4.1.1"
"lodash.kebabcase": "^4.1.1",
"react-loadable": "^5.5.0",
"tslib": "^1.9.3"
},
"devDependencies": {
"@blueeast/tslint-config-blueeast": "^0.5.6",
"@types/jest": "^23.1.6",
"@types/lodash.flowright": "^3.5.4",
"@types/lodash.isfunction": "^3.0.4",
"@types/lodash.isnil": "^4.0.4",
"@types/lodash.kebabcase": "^4.1.4",
"@types/node": "^10.9.4",
"@types/react": "^16.4.13",
"@types/react-loadable": "^5.4.1",
"codecov": "^3.0.2",
"commitizen": "^2.10.1",
"cz-conventional-changelog": "^2.1.0",
Expand Down
3 changes: 2 additions & 1 deletion src/BlueRain.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HookRegistry, PluginRegistry } from './registries';
import { ComponentRegistry, HookRegistry, PluginRegistry } from './registries';
import { Logger } from './api';

export class BlueRain {
Expand All @@ -7,6 +7,7 @@ export class BlueRain {
public Logger = new Logger(this);

// Registries
public Components: ComponentRegistry = new ComponentRegistry(this);
public Hooks: HookRegistry = new HookRegistry(this);
public Plugins: PluginRegistry = new PluginRegistry(this);
}
13 changes: 9 additions & 4 deletions src/api/BlueRainModule.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { MaybeEsModule, getDefiniteModule, isEsModule } from '../utils/modules';
import { MaybePromise, getDefinitePromise, isPromise } from '../utils/promises';

export type BlueRainModuleInput<T> = MaybeEsModule<MaybePromise<MaybeEsModule<T>>>;
export type BlueRainModuleInput<T> = MaybePromise<MaybeEsModule<T>>;
// export type BlueRainModuleInput<T> = MaybeEsModule<MaybePromise<MaybeEsModule<T>>>;

// export type MaybeBlueRainModule<T> = T | BlueRainModule<T>;
export type MaybeBlueRainModule<T> = T | BlueRainModule<T>;
Expand All @@ -10,11 +11,15 @@ export type MaybeBlueRainModuleOrInput<T> = BlueRainModuleInput<T> | MaybeBlueRa

export function getDefiniteBlueRainModule<T>(module: MaybeBlueRainModuleOrInput<T>): BlueRainModule<T> {

if (!(module instanceof BlueRainModule)) {
module = (new BlueRainModule(module)) as BlueRainModule<T>;
if (!isBlueRainModule(module)) {
module = new BlueRainModule(module as BlueRainModuleInput<T>);
}

return module;
return module as BlueRainModule<T>;
}

export function isBlueRainModule<T>(input: T) {
return (input instanceof BlueRainModule);
}

/**
Expand Down
88 changes: 88 additions & 0 deletions src/registries/ComponentRegistry/ComponentRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { ComponentInput, ComponentRegistryItem } from './types';
import { createComponentRegistryItem, isComponentRegistryItem } from './helpers';
import { BlueRain } from '../../BlueRain';
import { Registry } from '../Registry';
import { getAsyncComponent } from './getAsyncComponent';
import flowRight from 'lodash.flowright';

export class ComponentRegistry extends Registry<ComponentRegistryItem> {

// For proxy methods
[key: string]: any;

constructor(BR: BlueRain) {
super(BR);

// Create proxy to enable BR.Components.Name method
return new Proxy(this, {
get: (target, name, value) => {
if (typeof name === 'string' && typeof (this[name]) === 'undefined') {
if (typeof name === 'string') {

if (this.has(name)) {
return this.resolve(name);
}
throw Error(`Could not find "${name}" component. Did you forget to register it?`);
}
}

return Reflect.get(target, name, value);
}
});
}


/**
* Register a React Component
*
* "component" (2nd) param can be one of the following:
*
* 1. A RegistryItem with the following variations:
* 1a. ".rawComponent" prop is a react component
* 1b. ".rawComponent" prop is an ES Module
* 1c. ".rawComponent" prop is a promise
* 1d. ".rawComponent" prop is a BlueRain Module
*
* 2. A react compoment with following variations:
* 2a. Is a react component
* 2b. Is an ES module that has a react component in .default prop
* 2c. Is a promise that resolves a react component (In ES module or otherwise)
* 2d. Is a BlueRainModule that resolves a react component
*
* @param name
* @param component
*/
public async register(name: string, component: ComponentInput) {

// (Point 1) If the input component is an object of RegistryItem
if (isComponentRegistryItem(component)) {

this.set(name, createComponentRegistryItem({
...component,
rawComponent: component.rawComponent,
}));
return;
}

// (Point 2) If the input component react component
this.set(name, createComponentRegistryItem({
rawComponent: component as React.ComponentType<any>
}));
}


public resolve(name: string) {

const registryItem = super.get(name);

if (!registryItem) {
throw new Error(`Component "${name}" is registered.`);
}

const rawComponent = getAsyncComponent(registryItem.rawComponent.promise);

const hocs = registryItem.hocs.map(hoc => (Array.isArray(hoc) ? hoc[0](hoc[1]) : hoc));

return flowRight([...hocs])(rawComponent);
}
}
25 changes: 25 additions & 0 deletions src/registries/ComponentRegistry/getAsyncComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Loadable, { LoadingComponentProps } from 'react-loadable';
import React from 'react';

export function getAsyncComponent(componentPromise: Promise<React.ComponentType<any>>) {

return Loadable({
loader: () => componentPromise,

loading(props: LoadingComponentProps) {
if (props.error) {
return <div>Error! <button onClick={props.retry}>Retry</button></div>;
} else if (props.timedOut) {
return <div>Taking a long time... <button onClick={props.retry}>Retry</button></div>;
} else if (props.pastDelay) {
return <div>Loading...</div>;
} else {
return null;
}
},

render(Component: React.ComponentType<any>, props: any) {
return <Component {...props} />;
}
});
}
30 changes: 30 additions & 0 deletions src/registries/ComponentRegistry/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ComponentRegistryItem, ComponentRegistryItemInternal } from './types';
import { getDefiniteBlueRainModule } from '../../api';

export function createComponentRegistryItem(input: ComponentRegistryItemInternal): ComponentRegistryItem {

if (!input.rawComponent) {
// tslint:disable-next-line:max-line-length
throw Error(`"rawComponent" property is required to register a component. Please provide valid component while adding component.`);
}

const rawComponent = getDefiniteBlueRainModule(input.rawComponent);

return {
hocs: [],
isAsync: rawComponent.isAsync,
preload: false,

...input,

rawComponent,
};
}

/**
* Type guard to check if an object is a ComponentRegistryItem
* @param item
*/
export function isComponentRegistryItem(item: any): item is ComponentRegistryItem {
return (item as ComponentRegistryItem).rawComponent !== undefined;
}
1 change: 1 addition & 0 deletions src/registries/ComponentRegistry/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ComponentRegistry';
Loading

0 comments on commit 65da844

Please sign in to comment.