Skip to content

Commit

Permalink
Merge pull request #94 from Igorbek/f-esm
Browse files Browse the repository at this point in the history
Modernize the package (esm, tree-shaking)
  • Loading branch information
A-gambit committed Oct 16, 2023
2 parents 25fd110 + 10c1e4d commit d2c617d
Show file tree
Hide file tree
Showing 15 changed files with 212 additions and 291 deletions.
12 changes: 2 additions & 10 deletions packages/focal-atom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,19 @@
"version": "0.10.1",
"description": "FRP Atom: observables, immutable data and lenses",
"main": "dist/_cjs/src/index.js",
"module": "dist/_esm5/src/index.js",
"es2015": "dist/_esm2015/src/index.js",
"module": "dist/_esm2015/src/index.js",
"types": "dist/_cjs/src/index.d.ts",
"sideEffects": [
"./dist/_cjs/src/lens/json.js",
"./dist/_esm5/src/lens/json.js",
"./dist/_esm2015/src/lens/json.js"
],
"files": [
"dist/_cjs/src/",
"dist/_esm5/src/",
"dist/_esm2015/src/"
],
"scripts": {
"docs": "rm -rf ./docs && typedoc --out docs --theme minimal --ignoreCompilerErrors --tsconfig tsconfig.json",
"clean": "rm -rf ./dist",
"dev": "tsc -b -w tsconfig.build.cjs.json",
"build:cjs": "tsc -b tsconfig.build.cjs.json",
"build:es5": "tsc -b tsconfig.build.es5.json",
"build:es2015": "tsc -b tsconfig.build.es2015.json",
"build": "npm run clean && yarn build:cjs && yarn build:es5 && yarn build:es2015 && npm run lint",
"build": "npm run clean && yarn build:cjs && yarn build:es2015 && npm run lint",
"test": "jest",
"test:watch": "jest --watch",
"lint": "eslint './src/**/*.ts*' 'test/**/*.ts*' && tsc --noemit",
Expand Down
138 changes: 1 addition & 137 deletions packages/focal-atom/src/lens/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface Optic<TSource, T, U> {
// @TODO can't optic compose?
}

function createModify<TSource, T, U>(
export function createModify<TSource, T, U>(
getter: (s: TSource) => T,
setter: (v: U, s: TSource) => TSource
) {
Expand All @@ -17,19 +17,6 @@ function createModify<TSource, T, U>(
}
}

export namespace Optic {
export function optic<TSource, T, U>(
getter: (s: TSource) => T,
setter: (v: U, s: TSource) => TSource
): Optic<TSource, T, U> {
return {
get: getter,
set: setter,
modify: createModify(getter, setter)
}
}
}

// @NOTE lens and prism are monomorphic: can't change the type of
// focused value on update

Expand All @@ -51,126 +38,3 @@ export interface Prism<TSource, T> extends Optic<TSource, Option<T>, T> {
compose<U>(next: Lens<Option<T>, U>): Lens<TSource, U>
compose<U>(next: Prism<T, U>): Prism<TSource, U>
}

export namespace Prism {
export function create<TSource, T>(
getter: (s: TSource) => Option<T>,
setter: (v: T, s: TSource) => TSource
): Prism<TSource, T> {
return {
get: getter,
set: setter,
modify: createModify(getter, setter),

compose<U>(next: Lens<T, U> | Prism<T, U>): Prism<TSource, U> {
// no runtime dispatch – the implementation works for both
// lens and prism argument
return create(
(s: TSource) => {
const x = getter(s)
return x !== undefined
? next.get(x)
: undefined
},
(v: U, s: TSource) => {
const x = getter(s)
return x !== undefined
? setter(next.set(v, x), s)
: s
}
)
}
}
}
}

export namespace Lens {
/**
* Create a lens.
*
* @export
* @template O type of the source
* @template P type of the destination
* @param getter a getter function
* @param setter a setter function
* @returns a lens that operates by given getter and setter
*/
export function create<TSource, T>(
getter: (s: TSource) => T,
setter: (v: T, s: TSource) => TSource
): Lens<TSource, T> {
return {
get: getter,
set: setter,
modify: createModify(getter, setter),

compose<U>(next: Lens<T, U>): Lens<TSource, U> {
return create(
(s: TSource) => next.get(getter(s)),
(v: U, s: TSource) => setter(next.set(v, getter(s)), s)
)
}
}
}

/**
* Compose several lenses, where each subsequent lens' state type is the previous
* lens' output type.
*
* You need to explicitly say what will be the type of resulting lens, and you
* need to do it right as there are no guarantees at compile time.
*
* @static
* @template S the resulting lens' state
* @template A the resulting lens' output
*/
export function compose<T, U>(l: Lens<T, U>): Lens<T, U>

export function compose<T1, T2, U>(l1: Lens<T1, T2>, l2: Lens<T2, U>): Lens<T1, U>

export function compose<T1, T2, T3, U>(
l1: Lens<T1, T2>, l2: Lens<T2, T3>, l3: Lens<T3, U>
): Lens<T1, U>

export function compose<T1, T2, T3, T4, U>(
l1: Lens<T1, T2>, l2: Lens<T2, T3>, l3: Lens<T3, T4>, l4: Lens<T4, U>
): Lens<T1, U>

export function compose<T1, T2, T3, T4, T5, U>(
l1: Lens<T1, T2>, l2: Lens<T2, T3>, l3: Lens<T3, T4>, l4: Lens<T4, T5>, l5: Lens<T5, U>
): Lens<T1, U>

export function compose<T, U>(...lenses: Lens<any, any>[]): Lens<T, U>

export function compose<T, U>(...lenses: Lens<any, any>[]): Lens<T, U> {
if (lenses.length === 0) {
throw new TypeError('Can not compose zero lenses. You probably want `Lens.identity`.')
} else if (lenses.length === 1) {
return lenses[0]
} else {
let r = lenses[0]
lenses.slice(1).forEach(l => {
r = r.compose(l)
})
return r as Lens<T, U>
}
}

const _identity = create<any, any>(x => x, (x: any, _: any) => x)

/**
* The identity lens – a lens that reads and writes the object itself.
*/
export function identity<T>(): Lens<T, T> {
return _identity as Lens<T, T>
}

const _nothing = Prism.create(_ => undefined, (_: any, o: any) => o)

/**
* A lens that always returns `undefined` on `get` and does no change on `set`.
*/
export function nothing<TSource, T>() {
return _nothing as Prism<TSource, T>
}
}
28 changes: 9 additions & 19 deletions packages/focal-atom/src/lens/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,17 @@
*
* @module
*/
import {
Lens, Prism, Optic
} from './base'

// This import adds JSON-specific lens functions to the Lens
// namespace, in style of RxJS.
//
// It's probably not the best way (these magic imports in general),
// but so far I haven't found a better way to:
// 1) have everything in a single namespace (for convenient usage)
// and
// 2) have base Lens definitions and JSON Lens definitions in separate
// modules.
//
// But maybe there is a way to avoid this?
import './json'
import type { Lens as LensType, Prism as PrismType, Optic as OpticType } from './base'

export type Lens<TSource, T> = LensType<TSource, T>
export type Prism<TSource, T> = PrismType<TSource, T>
export type Optic<TSource, T, U> = OpticType<TSource, T, U>

export * as Optic from './optic'
export * as Prism from './prism'
export * as Lens from './lens'

export {
PropExpr
} from './json'

export {
Lens, Prism, Optic
}
Loading

0 comments on commit d2c617d

Please sign in to comment.