From 7ecb8dd8b3a86fa6a6ac3eef2f90863f3100a758 Mon Sep 17 00:00:00 2001 From: Barney Date: Fri, 10 Aug 2018 14:32:07 +0100 Subject: [PATCH] ESM tests run in the browser --- README.md | 50 +++++++++++++++++++++++++------------------- index.js | 6 +++--- tests/constant.html | 45 +++++++++++++++++++++++++++++++++++++++ tests/constant.js | 8 +++++-- tests/explicit.html | 50 ++++++++++++++++++++++++++++++++++++++++++++ tests/explicit.js | 7 +++++-- tests/immutable.html | 48 ++++++++++++++++++++++++++++++++++++++++++ tests/immutable.js | 7 +++++-- 8 files changed, 191 insertions(+), 30 deletions(-) create mode 100644 tests/constant.html create mode 100644 tests/explicit.html create mode 100644 tests/immutable.html diff --git a/README.md b/README.md index f19f3a2..9c8e5c2 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ A tool for making deep & subtle mutations on - or modified copies of - Javascrip Throw your rose-tinted [lenses](https://medium.com/javascript-inside/an-introduction-into-lenses-in-javascript-e494948d1ea5), [reducers](http://redux.js.org/docs/basics/Reducers.html) & [decorators](https://tc39.github.io/proposal-decorators/) out the window: Patchinko is an ECMAScript3-compliant utility that makes complex patching fast and easy, without the ceremony. * [What?](#what) - * API variations: - * [Explicit](#explicit) - * [Constant](#1-constant) + * [Explicit](#explicit) or + * [Overloaded](#explicit): + * [Constant](#1-constant) or * [Immutable](#2-immutable) * [Where?](#where) * [How?](#how) @@ -21,15 +21,10 @@ Throw your rose-tinted [lenses](https://medium.com/javascript-inside/an-introduc Patchinko exposes 4 explicit APIs: `P`, `S`, `PS`, & `D`. In general it's easier to work with the overloaded APIs, but explicit is instructive in getting a clear mental model of the different granular operations Patchinko performs under the hood. -`P` is like `Object.assign`: given `P(target, input1, input2, etc)`, it consumes inputs left to right and copies their properties onto the supplied target - -*…except that…* - -If any target properties are instances of `S(function)`, it will supply the scoped function with the target property for that key, and assign the result back to the target. - -If any target properties are `D`, it will delete the property of the same key on the target. - -`PS([ target, ] input)` is a composition of `P` & `S`, for when you need to patch recursively. If you supply a `target`, the original value will be left untouched (useful for immutable patching). +* `P` is like `Object.assign`: given `P(target, input1, input2, etc)`, it consumes inputs left to right and copies their properties onto the supplied target, *except that:* +* If any target properties are instances of `S(function)`, it will supply the scoped function with the target property for that key, and assign the result back to the target; +* If any target properties are `D`, it will delete the property of the same key on the target; +* `PS([ target, ] input)` is a composition of `P` & `S`, for when you need to patch recursively. If you supply a `target`, the original value will be left untouched (useful for immutable patching). ## Overloaded @@ -37,10 +32,10 @@ Patchinko also comes with a don't-make-me-think single-reference overloaded API `O` is an overloaded API that subsumes the above (with the exception of the n-ary immutable `PS` overload): -1. No arguments stands in for `D`. -2. A function argument stands in for `S`. -3. A non-function single argument stands in for `PS`. -4. …otherwise, `P`. +* No arguments stands in for `D` +* A function argument stands in for `S` +* A non-function single argument stands in for `PS` +* …otherwise, `P` The overloaded API comes in 2 flavours: @@ -53,14 +48,14 @@ The 1st variation of the overloaded API assumes you want to mutate the `target`s The 2nd works on a more functional basis: the `target`s of each operation are left intact and any changes result in new objects being produced as the result of each operation. This is the immutable approach. > #### ☝️ Why does it matter? - +> > If you're using Patchinko to monkey-patch an arbitrary third party API, you almost certainly want to mutate it: complex APIs may use 'instanceof' and equality reference checks internally; if you're patching a class / prototypal construct with internal and external references across the code-base, you need to preserve those references in order for everything to work as expected. - +> > But if you're using Patchinko to make changes to a data structure that's the sole business of your application's data model, that kind of stuff shouldn't be necessary - you can and should certainly avoid those patterns (they're complex and brittle!). In this scenario, creating new objects instead of mutating old ones can make the development & debugging process significantly easier: - +> > * Because the result of each patch operation is a new entity, you can store the results as new references and compare them later on. This can be useful when you want to see how a model has changed step by step over the course of several operations. > * Because nested structures within the patched entity that *haven't* been individually patched will retain their old identity, you an use [memoization](https://en.wikipedia.org/wiki/Memoization) to avoid unnecessary reactive computations. Traditionally this has been touted as a method for reactive Javascript applications - in particular virtual DOM libraries like [Mithril](https://mithril.js.org/) - to increase performance by skipping wasteful recomputations; but the salient advantage of this functionality is for debugging - you can set breakpoints far downstream in an application call graph and only pause script execution if and when change has occured. - +> > *When it comes to any defensive 'best practice' for the sake of performance - in the absence of any qualifiable evidence - the ability for authors & readers to reason & interact with the code lucidly should always be more jusdged more important to the architecture of code than any theories about what the computer might prefer.* # Where? @@ -71,19 +66,27 @@ In Node: ```js const {P, S, PS, D} = require('patchinko/explicit') + // or + const O = require('patchinko/constant') + // or + const O = require('patchinko/immutable') ``` With ESM: -```mjs +```js import {P, S, PS, D} from 'patchinko/explicit' + // or + import O from 'patchinko/constant' + // or + import O from 'patchinko/immutable' ``` @@ -92,10 +95,14 @@ In the browser: ```html + + + + ``` @@ -304,6 +311,7 @@ Bear in mind you can't return `P`, `PS`, or `D` operations from `S`. This is nev * ECMAScript modules * `overloaded` renamed to `constant` * All API variants exposed via entry point +* Browser-based ESM tests (`.html` files in tests folder) * Refactor tests to avoid symbols (they're unnecessary and misleading) * Troubleshooting documentation (+ tweaks) * Updated dependencies (+ API compliance tweaks) diff --git a/index.js b/index.js index d79328e..3507de9 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,7 @@ try { var explicit = require('./explicit') module.exports = { - P : explicit.D, + P : explicit.P, S : explicit.S, PS : explicit.PS, D : explicit.D, @@ -13,8 +13,8 @@ try { catch(e){ throw new Error( 'The environment attempted to load all Patchinko APIs via CommonJS, ' - + 'but the environment doesn\'t support the CommonJS API. \n' - + 'You probably need to fix a script tag to point to the specific file you need. \n' + + 'but the environment doesn\'t appear to support CommonJS - ' + + 'you probably need to fix a script tag to point to the specific file you need. \n' + 'For install & import instructions, see: \n' + 'https://github.com/barneycarroll/patchinko/blob/master/README.md#where \n' + 'Original error: \n' diff --git a/tests/constant.html b/tests/constant.html new file mode 100644 index 0000000..174c146 --- /dev/null +++ b/tests/constant.html @@ -0,0 +1,45 @@ + + + + + + + Patchinko test suite: constant.mjs + + + + + + + + + + +

+ ./ +

+ +

+ Patchinko test suite: constant.mjs +

+ +

Wait for it...

+ + diff --git a/tests/constant.js b/tests/constant.js index b74282c..0c67288 100644 --- a/tests/constant.js +++ b/tests/constant.js @@ -1,10 +1,14 @@ -const o = require('ospec') +try { + var o = require('ospec') -const O = require('../constant.js') + var O = require('../constant.js') +} +catch(e){} const I = x => x const A = f => x => f(x) + o.spec('Mutable overload API: ', () => { o('`O` (with a single function argument)', () => { const unique = {} diff --git a/tests/explicit.html b/tests/explicit.html new file mode 100644 index 0000000..c24333d --- /dev/null +++ b/tests/explicit.html @@ -0,0 +1,50 @@ + + + + + + + + Patchinko test suite: explicit.mjs + + + + + + + + + + +

+ ./ +

+ +

+ Patchinko test suite: + explicit.mjs +

+ +

Wait for it...

+ + + diff --git a/tests/explicit.js b/tests/explicit.js index 29b236e..6ca0ef6 100644 --- a/tests/explicit.js +++ b/tests/explicit.js @@ -1,6 +1,9 @@ -const o = require('ospec') +try { + var o = require('ospec') -const {P, S, PS, D} = require('../explicit.js') + var {P, S, PS, D} = require('../explicit.js') +} +catch(e){} const I = x => x const A = f => x => f(x) diff --git a/tests/immutable.html b/tests/immutable.html new file mode 100644 index 0000000..5c87ae1 --- /dev/null +++ b/tests/immutable.html @@ -0,0 +1,48 @@ + + + + + + + + Patchinko test suite: immutable.mjs + + + + + + + + + + +

+ ./ +

+ +

+ Patchinko test suite: + immutable.mjs +

+ +

Wait for it...

+ + + diff --git a/tests/immutable.js b/tests/immutable.js index a80fca3..52aa0dc 100644 --- a/tests/immutable.js +++ b/tests/immutable.js @@ -1,6 +1,9 @@ -const o = require('ospec') +try { + var o = require('ospec') -const O = require('../immutable.js') + var O = require('../immutable.js') +} +catch(e){} const I = x => x const A = f => x => f(x)