Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hoist proptypes #51

Merged
merged 2 commits into from Feb 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,6 @@
## 2.2.0
* Component generated by `connect` inherits propTypes unrelated to the store dependencies from the BaseComponent

## 2.1.1
* exports `connectCallback` from the index module

Expand Down
2 changes: 1 addition & 1 deletion bower.json
@@ -1,6 +1,6 @@
{
"name": "general-store",
"version": "2.1.1",
"version": "2.2.0",
"homepage": "https://github.com/HubSpot/general-store",
"authors": [
"Colby Rabideau <crabideau@hubspot.com>"
Expand Down
3 changes: 2 additions & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "general-store",
"version": "2.1.1",
"version": "2.2.0",
"description": "Simple, flexible store implementation for Flux.",
"main": "lib/GeneralStore.js",
"scripts": {
Expand All @@ -13,6 +13,7 @@
"clean": "rm -rf ./dist && rm -rf lib",
"lint": "eslint ./src",
"prepublish": "npm run build-and-test",
"test:unit": "jest",
"test": "npm run check && npm run lint && jest"
},
"repository": {
Expand Down
120 changes: 66 additions & 54 deletions src/dependencies/DependencyMap.js
@@ -1,42 +1,38 @@
/* @flow */
import { getActionTypes, getDispatchToken } from '../store/InspectStore';
import {getActionTypes, getDispatchToken} from '../store/InspectStore';
import invariant from 'invariant';
import {
oFilterMap,
oForEach,
oMap,
oMerge,
oReduce,
oReduce
} from '../utils/ObjectUtils';
import Store from '../store/Store';

export type CompoundDependency = {
propTypes?: Object,
stores: Array<Store>;
deref: (
props?: Object,
state?: ?Object,
stores?: Array<Store>
) => any;
stores: Array<Store>,
deref: (props?: Object, state?: ?Object, stores?: Array<Store>) => any
};

export type Dependency = CompoundDependency | Store;

export type DependencyIndexEntry = {
dispatchTokens: {[key:string]: bool};
fields: {[key:string]: bool};
dispatchTokens: {[key: string]: boolean},
fields: {[key: string]: boolean}
};

export type DependencyIndex = {
[key:string]: DependencyIndexEntry;
[key: string]: DependencyIndexEntry
};

export type DependencyMap = {
[key:string]: Dependency;
[key: string]: Dependency
};

export type PropTypes = {
[key:string]: Function;
[key: string]: Function
};

export function enforceValidDependencies(
Expand All @@ -57,7 +53,7 @@ export function enforceValidDependencies(
field,
dependency
);
const { deref, stores } = dependency;
const {deref, stores} = dependency;
invariant(
typeof deref === 'function',
'expected `%s.deref` to be a function but got `%s`',
Expand All @@ -83,17 +79,34 @@ export function enforceValidDependencies(
return dependencies;
}

export function dependencyPropTypes(dependencies: DependencyMap): PropTypes {
return oReduce(dependencies, (types, dependency) => {
if (dependency instanceof Store) {
return types;
}
const { propTypes } = dependency;
if (!propTypes || typeof propTypes !== 'object') {
return types;
}
return oMerge(types, propTypes);
}, {});
export function dependencyPropTypes(
dependencies: DependencyMap,
existingPropTypes: {[key: string]: Function} = {}
): PropTypes {
const unrelatedPropTypes = oReduce(
existingPropTypes,
(keep, type, name) => {
if (!dependencies.hasOwnProperty(name)) {
keep[name] = type;
}
return keep;
},
{}
);
return oReduce(
dependencies,
(types, dependency) => {
if (dependency instanceof Store) {
return types;
}
const {propTypes} = dependency;
if (!propTypes || typeof propTypes !== 'object') {
return types;
}
return oMerge(types, propTypes);
},
unrelatedPropTypes
);
}

export function calculate(
Expand All @@ -104,7 +117,7 @@ export function calculate(
if (dependency instanceof Store) {
return dependency.get();
}
const { deref, stores } = dependency;
const {deref, stores} = dependency;
if (deref.length === 0) {
return deref();
}
Expand All @@ -119,10 +132,7 @@ export function calculateInitial(
props: Object,
state: ?Object
): Object {
return oMap(
dependencies,
(dependency) => calculate(dependency, props, state)
);
return oMap(dependencies, dependency => calculate(dependency, props, state));
}

export function calculateForDispatch(
Expand All @@ -131,10 +141,8 @@ export function calculateForDispatch(
props: Object,
state: ?Object
): Object {
return oMap(
dependencyIndexEntry.fields,
(_, field) => calculate(dependencies[field], props, state),
);
return oMap(dependencyIndexEntry.fields, (_, field) =>
calculate(dependencies[field], props, state));
}

export function calculateForPropsChange(
Expand All @@ -144,8 +152,8 @@ export function calculateForPropsChange(
): Object {
return oFilterMap(
dependencies,
(dep) => dep.deref && dep.deref.length > 0,
(dep) => calculate(dep, props, state)
dep => dep.deref && dep.deref.length > 0,
dep => calculate(dep, props, state)
);
}

Expand All @@ -156,40 +164,44 @@ export function calculateForStateChange(
): Object {
return oFilterMap(
dependencies,
(dep) => dep.deref && dep.deref.length > 1,
(dep) => calculate(dep, props, state)
dep => dep.deref && dep.deref.length > 1,
dep => calculate(dep, props, state)
);
}

function makeIndexEntry(): DependencyIndexEntry {
return {
fields: {},
dispatchTokens: {},
dispatchTokens: {}
};
}

export function makeDependencyIndex(
dependencies: DependencyMap
): DependencyIndex {
enforceValidDependencies(dependencies);
return oReduce(dependencies, (index, dep, field) => {
const stores = dep instanceof Store ? [dep] : dep.stores;
stores.forEach((store) => {
getActionTypes(store).forEach((actionType) => {
let entry = index[actionType];
if (!entry) {
entry = index[actionType] = makeIndexEntry();
}
const token = getDispatchToken(store);
entry.dispatchTokens[token] = true;
entry.fields[field] = true;
return oReduce(
dependencies,
(index, dep, field) => {
const stores = dep instanceof Store ? [dep] : dep.stores;
stores.forEach(store => {
getActionTypes(store).forEach(actionType => {
let entry = index[actionType];
if (!entry) {
entry = index[actionType] = makeIndexEntry();
}
const token = getDispatchToken(store);
entry.dispatchTokens[token] = true;
entry.fields[field] = true;
});
});
});
return index;
}, {});
return index;
},
{}
);
}

export function dependenciesUseState(dependencies: DependencyMap): bool {
export function dependenciesUseState(dependencies: DependencyMap): boolean {
for (const field in dependencies) {
if (!dependencies.hasOwnProperty(field)) {
continue;
Expand Down