react-arbiter
Advanced tools
Comparing version 0.3.0 to 0.4.0
# React Arbiter Changelog | ||
## 0.4.0 | ||
- Improved documentation | ||
- Renamed types to avoid clashes | ||
- Renamed `getModules` to `fetchModules` | ||
- Added `getDependencies` prop | ||
- Added `openCache` utility | ||
## 0.3.0 | ||
@@ -4,0 +12,0 @@ |
import * as React from 'react'; | ||
import { Module, ArbiterDisplay, ArbiterOptions } from '../types'; | ||
import { ArbiterModule, ArbiterDisplay, ArbiterOptions } from '../types'; | ||
export interface ArbiterRecallProps<TApi> extends ArbiterOptions<TApi> { | ||
@@ -13,3 +13,3 @@ /** | ||
error?: any; | ||
modules: Array<Module<TApi>>; | ||
modules: Array<ArbiterModule<TApi>>; | ||
} | ||
@@ -16,0 +16,0 @@ /** |
@@ -26,5 +26,7 @@ "use strict"; | ||
componentDidMount() { | ||
const { getModules, dependencies, fetchDependency } = this.props; | ||
const { fetchModules, dependencies, getDependencies, fetchDependency, cache } = this.props; | ||
this.mounted = true; | ||
utils_1.loadModules(getModules, fetchDependency, dependencies).then(modules => this.mounted && this.finish(undefined, modules), error => this.mounted && this.finish(error, [])); | ||
if (typeof fetchModules === 'function') { | ||
utils_1.loadModules(fetchModules, fetchDependency, dependencies, getDependencies, cache).then(modules => this.mounted && this.finish(undefined, modules), error => this.mounted && this.finish(error, [])); | ||
} | ||
} | ||
@@ -31,0 +33,0 @@ componentWillUnmount() { |
import * as React from 'react'; | ||
import { Module, ArbiterOptions } from '../types'; | ||
import { ArbiterModule, ArbiterOptions } from '../types'; | ||
export interface RecallProps<TApi = {}> { | ||
loaded?: boolean; | ||
modules?: Array<Module<TApi>>; | ||
modules?: Array<ArbiterModule<TApi>>; | ||
error?: any; | ||
@@ -7,0 +7,0 @@ } |
import { ComponentType } from 'react'; | ||
export interface ModuleMetadata { | ||
export interface ArbiterModuleMetadata { | ||
/** | ||
* The name of the module, i.e., the package id. | ||
*/ | ||
name: string; | ||
/** | ||
* The version of the module. Should be semantically versioned. | ||
*/ | ||
version: string; | ||
name: string; | ||
/** | ||
* The dependency map of the module. | ||
*/ | ||
dependencies: { | ||
/** | ||
* The name of the dependency and the related location, which | ||
* defines where to get (i.e., fetch) it from. | ||
*/ | ||
[name: string]: string; | ||
}; | ||
/** | ||
* The content of the module. If the content is not available | ||
* the link will be used (unless caching has been activated). | ||
*/ | ||
content?: string; | ||
/** | ||
* The link for retrieving the content of the module. | ||
*/ | ||
link?: string; | ||
/** | ||
* The computed hash value of the module's content. Should be | ||
* accurate to allow caching. | ||
*/ | ||
hash: string; | ||
/** | ||
* If available indicates that the module should not be cached. | ||
* In case of a string this is interpreted as the expiration time | ||
* of the cache. In case of an accurate hash this should not be | ||
* required or set. | ||
*/ | ||
noCache?: boolean | string; | ||
} | ||
export interface ModuleApp<TApi> { | ||
setup(portal: TApi): void; | ||
export interface ArbiterModuleApp<TApi> { | ||
/** | ||
* Integrates the evaluated module into the application. | ||
* @param api The API to access the application. | ||
*/ | ||
setup(api: TApi): void; | ||
} | ||
export interface ModuleExports<TApi> { | ||
exports: ModuleApp<TApi> | undefined; | ||
export interface ArbiterModuleExports<TApi> { | ||
exports: ArbiterModuleApp<TApi> | undefined; | ||
} | ||
export declare type Module<TApi> = ModuleApp<TApi> & ModuleMetadata; | ||
export declare type ArbiterModule<TApi> = ArbiterModuleApp<TApi> & ArbiterModuleMetadata; | ||
export interface DependencyFetcher { | ||
/** | ||
* Defines how other dependencies are fetched. | ||
* @param url The URL to the dependency that should be fetched. | ||
* @returns The promise yielding the dependency's content. | ||
*/ | ||
(url: string): Promise<string>; | ||
} | ||
export interface ApiCreator<TApi> { | ||
/** | ||
* Creates an API for the given raw module. | ||
* @param target The raw (meta) content of the module. | ||
* @returns The API object to be used with the module. | ||
*/ | ||
(target: ArbiterModuleMetadata): TApi; | ||
} | ||
export interface DependencyGetter { | ||
/** | ||
* Gets the locally available dependencies for the specified | ||
* module. If this function is missing or returns false or undefined | ||
* the globally available dependencies will be used. | ||
* @returns The dependencies that should be used for evaluating the | ||
* module. | ||
*/ | ||
(target: ArbiterModuleMetadata): AvailableDependencies | undefined | false; | ||
} | ||
export interface ArbiterModuleFetcher { | ||
/** | ||
* Gets the raw modules from (e.g., a server) asynchronously. | ||
* @returns The promise yielding an array of raw modules. | ||
*/ | ||
(cachedModules: Array<ArbiterModuleMetadata>): Promise<Array<ArbiterModuleMetadata>>; | ||
} | ||
export interface ArbiterModuleCache { | ||
update(cachedModules: Array<ArbiterModuleMetadata>, receivedModules: Array<ArbiterModuleMetadata>): Promise<Array<ArbiterModuleMetadata>>; | ||
retrieve(): Promise<Array<ArbiterModuleMetadata>>; | ||
} | ||
export interface AvailableDependencies { | ||
@@ -29,3 +98,3 @@ [name: string]: any; | ||
export interface ArbiterDisplay<TApi> { | ||
(loaded: boolean, modules: Array<Module<TApi>>, error?: any): React.ReactNode; | ||
(loaded: boolean, modules: Array<ArbiterModule<TApi>>, error?: any): React.ReactNode; | ||
} | ||
@@ -55,31 +124,35 @@ export interface StasisOptions { | ||
/** | ||
* Creates an API for the given raw module. | ||
* @param target The raw (meta) content of the module. | ||
* @returns The API object to be used with the module. | ||
* The callback function for creating an API object. | ||
* The API object is passed on to a specific module. | ||
*/ | ||
createApi(target: ModuleMetadata): TApi; | ||
createApi: ApiCreator<TApi>; | ||
/** | ||
* Gets the raw modules from (e.g., a server) asynchronously. | ||
* @returns The promise yielding an array of raw modules. | ||
* The callback for fetching the dynamic modules. | ||
*/ | ||
getModules(): Promise<Array<ModuleMetadata>>; | ||
fetchModules: ArbiterModuleFetcher; | ||
/** | ||
* Optionally, some already existing evaluated modules, e.g., | ||
* helpful when debugging. | ||
* @returns An array of evaluated modules. | ||
*/ | ||
modules?: Array<Module<TApi>>; | ||
modules?: Array<ArbiterModule<TApi>>; | ||
/** | ||
* Defines how other dependencies are fetched. | ||
* @param url The URL to the dependency that should be fetched. | ||
* @returns The promise yielding the dependency's content. | ||
* The callback for defining how a dependency will be fetched. | ||
*/ | ||
fetchDependency?(url: string): Promise<string>; | ||
fetchDependency?: DependencyFetcher; | ||
/** | ||
* Gets a map of available locale dependencies for a module. | ||
* The dependencies are used during the evaluation. | ||
*/ | ||
getDependencies?: DependencyGetter; | ||
/** | ||
* Gets the map of globally available dependencies with their names | ||
* as keys and their evaluated module content as value. | ||
* @returns The optionally global dependencies. | ||
*/ | ||
dependencies?: AvailableDependencies; | ||
/** | ||
* Optionally uses the defined cache. For a default implementation | ||
* use the `openCache` method, which is based on IndexDB. | ||
*/ | ||
cache?: ArbiterModuleCache; | ||
} | ||
export declare type ComponentDefinition<T> = ComponentType<T> | RenderCallback<T>; |
@@ -1,6 +0,6 @@ | ||
import { ModuleMetadata, AvailableDependencies, Module } from '../types'; | ||
import { defaultFetchDependency } from './fetch'; | ||
import { AvailableDependencies, ArbiterModule, DependencyGetter, ApiCreator, ArbiterModuleFetcher, ArbiterModuleCache } from '../types'; | ||
/** | ||
* Loads the modules by first getting them, then evaluating the raw content. | ||
* @param getModules The function to resolve the modules. | ||
* @param fetchModules The function to resolve the modules. | ||
* @param fetchDependency A function to fetch the dependencies. By default, `fetch` is used. | ||
@@ -10,3 +10,3 @@ * @param dependencies The availablly global dependencies, if any. | ||
*/ | ||
export declare function loadModules<TApi>(getModules: () => Promise<Array<ModuleMetadata>>, fetchDependency?: typeof defaultFetchDependency, dependencies?: AvailableDependencies): Promise<Module<TApi>[]>; | ||
export declare function loadModules<TApi>(fetchModules: ArbiterModuleFetcher, fetchDependency?: typeof defaultFetchDependency, globalDependencies?: AvailableDependencies, getLocalDependencies?: DependencyGetter, cache?: ArbiterModuleCache): Promise<Array<ArbiterModule<TApi>>>; | ||
/** | ||
@@ -18,2 +18,2 @@ * Sets up the evaluated modules to become integrated modules. | ||
*/ | ||
export declare function setupModules<TApi>(createApi: (target: ModuleMetadata) => TApi, modules: Array<Module<TApi>>): Module<TApi>[]; | ||
export declare function setupModules<TApi>(createApi: ApiCreator<TApi>, modules: Array<ArbiterModule<TApi>>): ArbiterModule<TApi>[]; |
@@ -6,5 +6,15 @@ "use strict"; | ||
const setup_1 = require("./setup"); | ||
const defaultGlobalDependencies = {}; | ||
const defaultGetDependencies = () => false; | ||
const defaultCache = { | ||
retrieve() { | ||
return Promise.resolve([]); | ||
}, | ||
update(_, received) { | ||
return Promise.resolve(received); | ||
}, | ||
}; | ||
/** | ||
* Loads the modules by first getting them, then evaluating the raw content. | ||
* @param getModules The function to resolve the modules. | ||
* @param fetchModules The function to resolve the modules. | ||
* @param fetchDependency A function to fetch the dependencies. By default, `fetch` is used. | ||
@@ -14,5 +24,10 @@ * @param dependencies The availablly global dependencies, if any. | ||
*/ | ||
function loadModules(getModules, fetchDependency = fetch_1.defaultFetchDependency, dependencies = {}) { | ||
if (typeof getModules === 'function') { | ||
return Promise.resolve(getModules()).then(moduleData => Promise.all(moduleData.map(m => load_1.loadModule(m, fetchDependency, dependencies)))); | ||
function loadModules(fetchModules, fetchDependency = fetch_1.defaultFetchDependency, globalDependencies = defaultGlobalDependencies, getLocalDependencies = defaultGetDependencies, cache = defaultCache) { | ||
if (typeof fetchModules === 'function') { | ||
const getDependencies = target => { | ||
return getLocalDependencies(target) || globalDependencies; | ||
}; | ||
return Promise.resolve(cache.retrieve()).then(cachedModules => Promise.resolve(fetchModules(cachedModules || [])) | ||
.then(receivedModules => cache.update(cachedModules, receivedModules)) | ||
.then(moduleData => Promise.all(moduleData.map(m => load_1.loadModule(m, fetchDependency, getDependencies))))); | ||
} | ||
@@ -19,0 +34,0 @@ else { |
@@ -1,2 +0,2 @@ | ||
import { ModuleApp, AvailableDependencies } from '../types'; | ||
import { ArbiterModuleApp, AvailableDependencies } from '../types'; | ||
/** | ||
@@ -9,3 +9,3 @@ * Compiles the given content from a generic dependency. | ||
*/ | ||
export declare function evalDependency<TApi>(name: string, content: string, dependencies?: AvailableDependencies): ModuleApp<TApi> | undefined; | ||
export declare function evalDependency<TApi>(name: string, content: string, dependencies?: AvailableDependencies): ArbiterModuleApp<TApi> | undefined; | ||
/** | ||
@@ -18,2 +18,2 @@ * Compiles the given content from a module with a dependency resolution. | ||
*/ | ||
export declare function compileDependency<TApi>(name: string, content: string, dependencies: AvailableDependencies): ModuleApp<TApi>; | ||
export declare function compileDependency<TApi>(name: string, content: string, dependencies: AvailableDependencies): ArbiterModuleApp<TApi>; |
export * from './aggregate'; | ||
export * from './cache'; | ||
export * from './dependency'; | ||
@@ -3,0 +4,0 @@ export * from './fetch'; |
@@ -7,2 +7,3 @@ "use strict"; | ||
__export(require("./aggregate")); | ||
__export(require("./cache")); | ||
__export(require("./dependency")); | ||
@@ -9,0 +10,0 @@ __export(require("./fetch")); |
@@ -1,2 +0,2 @@ | ||
import { ModuleMetadata, Module, DependencyFetcher, AvailableDependencies } from '../types'; | ||
import { ArbiterModuleMetadata, ArbiterModule, DependencyFetcher, DependencyGetter } from '../types'; | ||
/** | ||
@@ -10,2 +10,2 @@ * Loads the given raw module content by resolving its dependencies and | ||
*/ | ||
export declare function loadModule<TApi>(meta: ModuleMetadata, fetchDependency: DependencyFetcher, dependencies: AvailableDependencies): Promise<Module<TApi>>; | ||
export declare function loadModule<TApi>(meta: ArbiterModuleMetadata, fetchDependency: DependencyFetcher, getDependencies: DependencyGetter): Promise<ArbiterModule<TApi>>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const dependency_1 = require("./dependency"); | ||
function loadDependencies(meta, fetchDependency, globalDependencies) { | ||
const dependencies = Object.assign({}, globalDependencies); | ||
function createEmptyModule(meta) { | ||
return Object.assign({}, meta, { setup() { } }); | ||
} | ||
function loadDependencies(meta, fetchDependency, getDependencies) { | ||
const dependencies = Object.assign({}, (getDependencies(meta) || {})); | ||
const existingDependencies = Object.keys(dependencies); | ||
@@ -21,6 +24,9 @@ const dependencyMap = Object.keys(meta.dependencies || {}) | ||
} | ||
function loadFromContent(meta, content, fetchDependency, dependencies) { | ||
return loadDependencies(meta, fetchDependency, dependencies).then(dependencies => { | ||
function loadFromContent(meta, content, fetchDependency, getDependencies) { | ||
return loadDependencies(meta, fetchDependency, getDependencies).then(dependencies => { | ||
const app = dependency_1.compileDependency(meta.name, content, dependencies); | ||
return Object.assign({}, app, meta); | ||
}, error => { | ||
console.error(`Could not load the dependencies of ${meta.name}.`, error); | ||
return createEmptyModule(meta); | ||
}); | ||
@@ -36,9 +42,9 @@ } | ||
*/ | ||
function loadModule(meta, fetchDependency, dependencies) { | ||
function loadModule(meta, fetchDependency, getDependencies) { | ||
const { link, content } = meta; | ||
if (link) { | ||
return fetchDependency(link).then(content => loadFromContent(meta, content, fetchDependency, dependencies)); | ||
return fetchDependency(link).then(content => loadFromContent(meta, content, fetchDependency, getDependencies)); | ||
} | ||
else if (content) { | ||
return loadFromContent(meta, content, fetchDependency, dependencies); | ||
return loadFromContent(meta, content, fetchDependency, getDependencies); | ||
} | ||
@@ -48,4 +54,4 @@ else { | ||
} | ||
return Promise.resolve(Object.assign({}, meta, { setup() { } })); | ||
return Promise.resolve(createEmptyModule(meta)); | ||
} | ||
exports.loadModule = loadModule; |
@@ -1,2 +0,2 @@ | ||
import { Module } from '../types'; | ||
import { ArbiterModule } from '../types'; | ||
/** | ||
@@ -8,2 +8,2 @@ * Sets up the given module by calling the exported `setup` function | ||
*/ | ||
export declare function setupModule<TApi>(app: Module<TApi>, api: TApi): void; | ||
export declare function setupModule<TApi>(app: ArbiterModule<TApi>, api: TApi): void; |
@@ -14,5 +14,5 @@ "use strict"; | ||
catch (e) { | ||
console.error(`Error while setting up ${app.name}.`, e); | ||
console.error(`Error while setting up ${app && app.name}.`, e); | ||
} | ||
} | ||
exports.setupModule = setupModule; |
@@ -11,2 +11,2 @@ import * as React from 'react'; | ||
*/ | ||
export declare function wrapComponent<T, K extends keyof T>(value: ComponentDefinition<T>, options?: WrapOptions<T, K>): React.FunctionComponent<Exclude<T, never>> | React.ComponentClass<{}, any>; | ||
export declare function wrapComponent<T, K extends keyof T>(value: ComponentDefinition<T>, options?: WrapOptions<T, K>): React.ComponentType<{}>; |
{ | ||
"name": "react-arbiter", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"description": "Recall all your modules to extend your SPA dynamically at runtime.", | ||
@@ -34,20 +34,20 @@ "main": "dist/index.js", | ||
"devDependencies": { | ||
"@types/enzyme": "^3.1.15", | ||
"@types/enzyme": "^3.1.17", | ||
"@types/enzyme-adapter-react-16": "^1.0.3", | ||
"@types/jest": "^23.3.12", | ||
"@types/react": "^16.7.18", | ||
"@types/react-dom": "^16.0.11", | ||
"@types/jest": "^24.0.3", | ||
"@types/react": "^16.8.2", | ||
"@types/react-dom": "^16.8.0", | ||
"enzyme": "^3.8.0", | ||
"enzyme-adapter-react-16": "^1.7.1", | ||
"enzyme-adapter-react-16": "^1.9.1", | ||
"enzyme-to-json": "^3.3.5", | ||
"jest": "^23.6.0", | ||
"jest-cli": "^23.6.0", | ||
"prettier": "^1.15.3", | ||
"react": "^16.7.0", | ||
"react-dom": "^16.7.0", | ||
"jest": "^24.1.0", | ||
"jest-cli": "^24.1.0", | ||
"prettier": "^1.16.4", | ||
"react": "^16.8.1", | ||
"react-dom": "^16.8.1", | ||
"ts-jest": "^23.10.5", | ||
"tslint": "^5.12.0", | ||
"tslint-config-prettier": "^1.17.0", | ||
"tslint": "^5.12.1", | ||
"tslint-config-prettier": "^1.18.0", | ||
"tslint-plugin-prettier": "^2.0.1", | ||
"typescript": "^3.2.2" | ||
"typescript": "^3.3.3" | ||
}, | ||
@@ -54,0 +54,0 @@ "peerDependencies": { |
@@ -30,3 +30,3 @@ # ![React Arbiter](docs/logo.png) | ||
function getModules() { | ||
function fetchModules() { | ||
//get a list of the available modules, potentially with content | ||
@@ -37,3 +37,3 @@ return fetch('/your/modules'); | ||
const App = ( | ||
<ArbiterRecall createApi={createApi} getModules={getModules}> | ||
<ArbiterRecall createApi={createApi} fetchModules={fetchModules}> | ||
<YourComponent /> | ||
@@ -47,3 +47,3 @@ </ArbiterRecall> | ||
```ts | ||
interface ModuleMetadata { | ||
interface ArbiterModuleMetadata { | ||
version: string; | ||
@@ -118,3 +118,3 @@ name: string; | ||
Important: The `wrapComponent` only supports React SFCs if they have the `displayName` property properly set (see above). Otherwise, this helper function cannot distinguish between a foreign and a React component and will therefore choose the foreign component. | ||
**Important**: The `wrapComponent` only supports React SFCs if they have the `displayName` property properly set (see above). Otherwise, this helper function cannot distinguish between a foreign and a React component and will therefore choose the foreign component. | ||
@@ -121,0 +121,0 @@ ## License |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
71875
45
961