Skip to content

Commit 857b45c

Browse files
committed
Add lens composition helper
1 parent 551efb1 commit 857b45c

File tree

4 files changed

+84
-40
lines changed

4 files changed

+84
-40
lines changed

.nojekyll

Whitespace-only changes.

package-lock.json

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
"typings": "lib/es6/index.d.ts",
88
"scripts": {
99
"build": "tsc -d && tsc -d --module ES6 --outDir lib/es6",
10-
"documentation": "typedoc --out docs src && cp-cli .nojekyll docs/.nojekyll",
11-
"prepublishOnly": "npm run build && npm run documentation"
10+
"format": "prettier --tab-width 4 --single-quote --write './src/**/*.{js,ts,tsx}'",
11+
"documentation": "typedoc --out docs src && touch docs/.nojekyll",
12+
"prepublishOnly": "npm run format && npm run build && npm run documentation"
1213
},
1314
"keywords": [
1415
"cyclejs",
@@ -18,6 +19,7 @@
1819
"license": "MIT",
1920
"devDependencies": {
2021
"cp-cli": "^1.0.2",
22+
"prettier": "^1.8.2",
2123
"release-it": "^3.1.2",
2224
"typedoc": "^0.9.0",
2325
"typescript": "^2.5.3",

src/index.ts

Lines changed: 73 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import xs, { Stream } from 'xstream';
2-
import { Instances } from 'cycle-onionify';
2+
import { Instances, Lens } from 'cycle-onionify';
33

44
export type Sinks = any;
55
export type Sources = any;
@@ -15,29 +15,35 @@ export interface MergeExceptions {
1515
* @param {MergeExceptions} exceptions a dictionary of special channels, e.g. DOM
1616
* @return {Sinks} the new unified sink
1717
*/
18-
export function mergeSinks(sinks: Sinks[], exceptions: MergeExceptions = {}): Sinks
19-
{
20-
const drivers : string[] = sinks
18+
export function mergeSinks(
19+
sinks: Sinks[],
20+
exceptions: MergeExceptions = {}
21+
): Sinks {
22+
const drivers: string[] = sinks
2123
.map(Object.keys)
2224
.reduce((acc, curr) => acc.concat(curr), [])
23-
.reduce((acc, curr) => acc.indexOf(curr) === -1 ? [...acc, curr] : acc, []);
24-
25-
const emptySinks : any = drivers
26-
.map(s => ({ [s]: [] }) as any)
27-
.reduce((acc, curr) => Object.assign(acc, curr ), {});
28-
29-
const combinedSinks = sinks
30-
.reduce((acc, curr) => {
31-
return Object.keys(acc)
32-
.map(s => ({ [s]: acc[s]}))
33-
.map(o => {
34-
const name : string = Object.keys(o)[0];
35-
return !curr[name] ? o : {
36-
[name]: [...o[name], curr[name]]
37-
};
38-
})
39-
.reduce((a, c) => Object.assign(a, c), {});
40-
}, emptySinks);
25+
.reduce(
26+
(acc, curr) => (acc.indexOf(curr) === -1 ? [...acc, curr] : acc),
27+
[]
28+
);
29+
30+
const emptySinks: any = drivers
31+
.map(s => ({ [s]: [] } as any))
32+
.reduce((acc, curr) => Object.assign(acc, curr), {});
33+
34+
const combinedSinks = sinks.reduce((acc, curr) => {
35+
return Object.keys(acc)
36+
.map(s => ({ [s]: acc[s] }))
37+
.map(o => {
38+
const name: string = Object.keys(o)[0];
39+
return !curr[name]
40+
? o
41+
: {
42+
[name]: [...o[name], curr[name]]
43+
};
44+
})
45+
.reduce((a, c) => Object.assign(a, c), {});
46+
}, emptySinks);
4147

4248
const merged = Object.keys(combinedSinks)
4349
.filter(name => Object.keys(exceptions).indexOf(name) === -1)
@@ -49,7 +55,8 @@ export function mergeSinks(sinks: Sinks[], exceptions: MergeExceptions = {}): Si
4955
.filter(([_, arr]) => arr !== undefined)
5056
.map(([key, arr]) => ({ [key]: exceptions[key](arr) }));
5157

52-
return merged.concat(special)
58+
return merged
59+
.concat(special)
5360
.reduce((acc, curr) => Object.assign(acc, curr), {});
5461
}
5562

@@ -61,18 +68,23 @@ export interface PickMergeExceptions {
6168
* Just like mergeSinks, but for onionify collections
6269
* @see mergeSinks
6370
*/
64-
export function pickMergeSinks(driverNames: string[], exceptions: PickMergeExceptions = {}): (ins: Instances<any>) => Sinks {
71+
export function pickMergeSinks(
72+
driverNames: string[],
73+
exceptions: PickMergeExceptions = {}
74+
): (ins: Instances<any>) => Sinks {
6575
return instances => {
6676
const merged: Sinks = driverNames
6777
.filter(name => Object.keys(exceptions).indexOf(name) === -1)
6878
.map(name => ({ [name]: instances.pickMerge(name) }));
6979

70-
const special = Object.keys(exceptions)
71-
.map(key => ({ [key]: exceptions[key](instances) }));
80+
const special = Object.keys(exceptions).map(key => ({
81+
[key]: exceptions[key](instances)
82+
}));
7283

73-
return merged.concat(special)
74-
.reduce((acc, curr) => Object.assign(acc, curr ), {});
75-
}
84+
return merged
85+
.concat(special)
86+
.reduce((acc, curr) => Object.assign(acc, curr), {});
87+
};
7688
}
7789

7890
/**
@@ -81,14 +93,16 @@ export function pickMergeSinks(driverNames: string[], exceptions: PickMergeExcep
8193
* @param {string[]} driverNames the names of all drivers that are possibly in the stream, it's best to use Object.keys() on your driver object
8294
* @return {Sinks} A sinks containing the streams of the last emission in the sinks$
8395
*/
84-
export function extractSinks(sinks$ : Stream<Sinks>, driverNames : string[]) : Sinks
85-
{
96+
export function extractSinks(
97+
sinks$: Stream<Sinks>,
98+
driverNames: string[]
99+
): Sinks {
86100
return driverNames
87101
.map(d => ({
88-
[d]: sinks$
89-
.map<Stream<any> | undefined>(s => s[d])
90-
.filter(b => !!b)
91-
.flatten()
102+
[d]: sinks$
103+
.map<Stream<any> | undefined>(s => s[d])
104+
.filter(b => !!b)
105+
.flatten()
92106
}))
93107
.reduce((acc, curr) => Object.assign(acc, curr), {});
94108
}
@@ -100,12 +114,34 @@ export function extractSinks(sinks$ : Stream<Sinks>, driverNames : string[]) : S
100114
* @param {string} name The name of the export. For loading a default export simply ignore
101115
* @return {Component} A dummy that loads the actual component
102116
*/
103-
export function loadAsync(moduleLoader: () => any, driverNames: string[], name: string = 'default'): Component {
117+
export function loadAsync(
118+
moduleLoader: () => any,
119+
driverNames: string[],
120+
name: string = 'default'
121+
): Component {
104122
return sources => {
105-
const lazyComponent$: Stream<Sinks> = xs.fromPromise(moduleLoader())
123+
const lazyComponent$: Stream<Sinks> = xs
124+
.fromPromise(moduleLoader())
106125
.map(m => m[name])
107126
.map(m => m(sources));
108127

109128
return extractSinks(lazyComponent$, driverNames);
110129
};
111130
}
131+
132+
/**
133+
* Composes two lenses to one
134+
* @param {Lens<A, B>} outer
135+
* @param {Lens<B, C>} inner
136+
* @return {Lens<A, C>} composed lens
137+
*/
138+
export function composeLenses<A, B, C>(
139+
outer: Lens<A, B>,
140+
inner: Lens<B, C>
141+
): Lens<A, C> {
142+
return {
143+
get: parent => inner.get(outer.get(parent)),
144+
set: (parent, child) =>
145+
outer.set(parent, inner.set(outer.get(parent), child))
146+
};
147+
}

0 commit comments

Comments
 (0)