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

Modernize the package (esm, tree-shaking) #94

Merged
merged 2 commits into from
Oct 16, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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