diff --git a/.flowconfig b/.flowconfig index 52d3b64d..0c3749f9 100644 --- a/.flowconfig +++ b/.flowconfig @@ -12,3 +12,4 @@ module.system.node.resolve_dirname=src /.*/gatsby-node.js /packages/dataparcels-docs/.* /node_modules/react-flip-move/src/.* +/node_modules/immutable/.* diff --git a/.size-limit b/.size-limit new file mode 100644 index 00000000..7a3bbb2a --- /dev/null +++ b/.size-limit @@ -0,0 +1,14 @@ +[ + { + limit: "25 KB", + path: "packages/dataparcels/lib/index.js" + }, + { + limit: "25 KB", + path: "packages/dataparcels/ParcelShape.js" + }, + { + limit: "25 KB", + path: "packages/react-dataparcels/lib/index.js" + } +] diff --git a/flow-typed/brokenModules.js b/flow-typed/brokenModules.js new file mode 100644 index 00000000..e24e7a69 --- /dev/null +++ b/flow-typed/brokenModules.js @@ -0,0 +1,3 @@ +declare module "immutable" { + declare module.exports: any +} diff --git a/jest.config.js b/jest.config.js index 96733b5f..b695422a 100644 --- a/jest.config.js +++ b/jest.config.js @@ -8,5 +8,13 @@ module.exports = { "!packages/dataparcels-docs/**" ], testMatch: ["**/__test__/**/*-test.js?(x)"], - testURL: 'http://localhost' + testURL: 'http://localhost', + coverageThreshold: { + global: { + branches: 90, + functions: 90, + lines: 90, + statements: 90 + } + } }; diff --git a/lerna.json b/lerna.json index 257c8584..55ba91a5 100644 --- a/lerna.json +++ b/lerna.json @@ -2,5 +2,5 @@ "lerna": "2.9.0", "npmClient": "yarn", "useWorkspaces": true, - "version": "0.17.2" + "version": "0.18.0-2" } diff --git a/package.json b/package.json index 1693a169..c7926557 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,10 @@ "flow": "blueflag-test flow", "flow-coverage": "blueflag-test flow-coverage -M", "lint": "blueflag-test lint --monorepo", - "test": "yarn jest --maxWorkers=4", - "test-all": "yarn lint && yarn flow && yarn test --maxWorkers=4 && yarn flow-coverage", + "test": "yarn build && yarn jest --maxWorkers=1", + "test-all": "yarn build && yarn test --maxWorkers=1 && yarn flow && yarn lint && yarn flow-coverage && yarn size-limit", "deploy-docs": "yarn lerna --scope dataparcels-docs run deploy", - "view-coverage": "yarn run coverage; open ./coverage/lcov-report/index.html", + "view-coverage": "open ./coverage/lcov-report/index.html", "watch": "lerna run watch --parallel --" }, "workspaces": { diff --git a/packages/dataparcels-docs/gatsby-node.js b/packages/dataparcels-docs/gatsby-node.js index 9a6fa9f7..e8e64120 100644 --- a/packages/dataparcels-docs/gatsby-node.js +++ b/packages/dataparcels-docs/gatsby-node.js @@ -1,68 +1,5 @@ const {createFilePath} = require('gatsby-source-filesystem'); const path = require('path'); -const fs = require('fs'); - -exports.createPages = ({graphql, boundActionCreators}) => { - const {createPage} = boundActionCreators; - - // function createExamples() { - // return graphql(` - // { - // allFile(filter: {sourceInstanceName: {eq: "example-pages"}}, sort: {fields: relativePath, order: ASC}) { - // edges { - // next { - // name - // relativePath - // } - // node { - // name - // relativePath - // } - // previous { - // name - // relativePath - // } - // } - // } - // } - // `) - // .then(result => { - // if (result.errors) { - // return Promise.reject(result.errors); - // } - - // let getPath = (node) => `/examples/${node.name.split("-")[1]}`; - // result.data.allFile.edges.forEach(({next, node, previous}, index) => { - // let component = path.resolve(`src/examples/${node.relativePath}`); - - // createPage({ - // path: getPath(node), - // component, - // context: { - // next: next ? getPath(next) : null, - // previous: previous ? getPath(previous) : null, - // file: node.relativePath - // } - // }); - // }); - // }); - // } - - return Promise.resolve() - //.then(createExamples) - ; -}; - -const circleYml = ` -general: - branches: - ignore: - - gh-pages -`; - -exports.onPostBuild = () => { - fs.writeFileSync(`${__dirname}/public/circle.yml`, circleYml); -} exports.modifyWebpackConfig = ({ config, stage }) => { diff --git a/packages/dataparcels-docs/package.json b/packages/dataparcels-docs/package.json index 3c18b917..970a39e4 100644 --- a/packages/dataparcels-docs/package.json +++ b/packages/dataparcels-docs/package.json @@ -1,6 +1,6 @@ { "name": "dataparcels-docs", - "version": "0.17.1", + "version": "0.18.0-2", "description": "Dataparcels Documentation", "author": "Damien Clarke", "license": "MIT", @@ -8,7 +8,6 @@ "scripts": { "build-all": "yarn build-docs", "build-docs": "gatsby build", - "deploy": "gatsby build --prefix-paths && gh-pages -d public", "watch": "gatsby develop", "test": "echo \"Error: no test specified\" && exit 1" }, @@ -24,7 +23,6 @@ "gatsby-remark-prismjs": "^1.2.21", "gatsby-source-filesystem": "^1.5.26", "gatsby-transformer-remark": "^1.7.34", - "gh-pages": "^1.1.0", "immutable": "^3.8.2", "mdx-loader": "1.0.0-beta.3", "mdxc": "^1.1.1", @@ -33,6 +31,7 @@ "react-flip-move": "^3.0.2", "react-helmet": "^5.2.0", "react-lifecycles-compat": "^3.0.4", + "react-sortable-hoc": "^1.4.0", "stampy": "^0.41.0", "unmutable": "^0.29.2" } diff --git a/packages/dataparcels-docs/src/component/APINavigation.jsx b/packages/dataparcels-docs/src/component/APINavigation.jsx index da66d33f..85965e5f 100644 --- a/packages/dataparcels-docs/src/component/APINavigation.jsx +++ b/packages/dataparcels-docs/src/component/APINavigation.jsx @@ -9,6 +9,7 @@ export default () => - ParcelHoc - ParcelBoundary - ParcelBoundaryHoc + - ParcelShape - ChangeRequest - Action ; diff --git a/packages/dataparcels-docs/src/content/API.js b/packages/dataparcels-docs/src/content/API.js index a9f414b9..7bdce536 100644 --- a/packages/dataparcels-docs/src/content/API.js +++ b/packages/dataparcels-docs/src/content/API.js @@ -31,7 +31,7 @@ export default () => name="Parcel" description={ Parcel is a data container. - It's job is to hold your data, split it into smaller parts, and merge changes back together. + Its job is to hold your data, split it into smaller parts, and merge changes back together. } image={IconParcel} /> @@ -39,7 +39,7 @@ export default () => name="ParcelHoc" description={ ParcelHoc is a React higher order component. - It's job is to provide a parcel as a prop, and to handle how the parcel binds to React props and lifecycle events. + Its job is to provide a parcel as a prop, and to handle how the parcel binds to React props and lifecycle events. } image={IconParcelHoc} /> @@ -47,7 +47,7 @@ export default () => name="ParcelBoundary" description={ ParcelBoundary is a React component. - It's job is to optimise rendering performance, and to optionally control the flow of parcel changes. + Its job is to optimise rendering performance, and to optionally control the flow of parcel changes. } image={IconParcelBoundary} /> @@ -55,9 +55,9 @@ export default () => name="ParcelBoundaryHoc" description={ ParcelBoundaryHoc is a React higher order component. - It's job is to control the flow of parcel changes. It is the higher order component version of a ParcelBoundary. + Its job is to control the flow of parcel changes. It is the higher order component version of a ParcelBoundary. } image={IconParcelBoundaryHoc} /> - See also: ChangeRequest, Action. + See also: ParcelShape, ChangeRequest, Action. ; diff --git a/packages/dataparcels-docs/src/content/APIExamples.md b/packages/dataparcels-docs/src/content/APIExamples.md index 6f8303ee..c8943fed 100644 --- a/packages/dataparcels-docs/src/content/APIExamples.md +++ b/packages/dataparcels-docs/src/content/APIExamples.md @@ -5,7 +5,8 @@ import Link from 'gatsby-link'; ### Parcel * Editing objects -* Editing arrays +* Editing arrays (including drag & drop) +* Parcel meta * Managing your own parcel state ### ParcelHoc diff --git a/packages/dataparcels-docs/src/content/Examples.md b/packages/dataparcels-docs/src/content/Examples.md deleted file mode 100644 index e17d4a0d..00000000 --- a/packages/dataparcels-docs/src/content/Examples.md +++ /dev/null @@ -1,6 +0,0 @@ -import Link from 'gatsby-link'; - -* Editing objects -* Editing arrays -* Managing your own parcel state -* Storing parcel state in the query string diff --git a/packages/dataparcels-docs/src/content/ExamplesParcelBoundary.md b/packages/dataparcels-docs/src/content/ExamplesParcelBoundary.md index fc99ccf9..4751a623 100644 --- a/packages/dataparcels-docs/src/content/ExamplesParcelBoundary.md +++ b/packages/dataparcels-docs/src/content/ExamplesParcelBoundary.md @@ -1,6 +1,6 @@ import Link from 'gatsby-link'; -* ParcelBoundary example +* ParcelBoundary pure rendering example * Using debounce * Using forceUpdate * Using hold diff --git a/packages/dataparcels-docs/src/content/parcelshape.gif b/packages/dataparcels-docs/src/content/parcelshape.gif new file mode 100644 index 00000000..2add1601 Binary files /dev/null and b/packages/dataparcels-docs/src/content/parcelshape.gif differ diff --git a/packages/dataparcels-docs/src/docs/api/action/Action.md b/packages/dataparcels-docs/src/docs/api/action/Action.md index 429ce91d..dde96034 100644 --- a/packages/dataparcels-docs/src/docs/api/action/Action.md +++ b/packages/dataparcels-docs/src/docs/api/action/Action.md @@ -3,8 +3,8 @@ import Link from 'component/Link'; # Action ```js -import {Action} from 'dataparcels'; -import {Action} from 'react-dataparcels'; +import Action from 'dataparcels/Action'; +import Action from 'react-dataparcels/Action'; ``` ```flow diff --git a/packages/dataparcels-docs/src/docs/api/action/isValueAction.md b/packages/dataparcels-docs/src/docs/api/action/isValueAction.md index 3d9b29a7..aad95f5f 100644 --- a/packages/dataparcels-docs/src/docs/api/action/isValueAction.md +++ b/packages/dataparcels-docs/src/docs/api/action/isValueAction.md @@ -4,4 +4,4 @@ isValueAction(): boolean Returns true if the action affects the original parcel's value. -Actions such as `setMeta` and `ping` do not affect the original parcel's value and are not value actions. +Actions such as `setMeta` do not affect the original parcel's value and are not value actions. diff --git a/packages/dataparcels-docs/src/docs/api/action/shouldBeSynchronous.md b/packages/dataparcels-docs/src/docs/api/action/shouldBeSynchronous.md deleted file mode 100644 index 9a75aa7a..00000000 --- a/packages/dataparcels-docs/src/docs/api/action/shouldBeSynchronous.md +++ /dev/null @@ -1,5 +0,0 @@ -```flow -shouldBeSynchronous(): boolean -``` - -Some types of actions (such as `ping`) are "synchronous", meaning that they will ignore debouncing and buffering, propagating immediately to the top level parcel when included in a ChangeRequest. This method returns true if the action is synchronous. diff --git a/packages/dataparcels-docs/src/docs/api/changeRequest/ChangeRequest.md b/packages/dataparcels-docs/src/docs/api/changeRequest/ChangeRequest.md index d11090c1..9fb62243 100644 --- a/packages/dataparcels-docs/src/docs/api/changeRequest/ChangeRequest.md +++ b/packages/dataparcels-docs/src/docs/api/changeRequest/ChangeRequest.md @@ -3,8 +3,8 @@ import Link from 'component/Link'; # ChangeRequest ```js -import {ChangeRequest} from 'dataparcels'; -import {ChangeRequest} from 'react-dataparcels'; +import ChangeRequest from 'dataparcels/ChangeRequest'; +import ChangeRequest from 'react-dataparcels/ChangeRequest'; ``` ```flow @@ -17,4 +17,4 @@ When a change occurs, ChangeRequests are used by Parcels to describe what to cha ChangeRequests contain an array of Actions to perform. -ChangeRequests can most often be accessed in `handleChange` and `modifyChange` functions. Most of the time these operate invisibly, and it's extremely rare that you'll create these yourself. +ChangeRequests can most often be accessed in `handleChange` and `modifyUp` functions. Most of the time these operate invisibly, and it's extremely rare that you'll create these yourself. diff --git a/packages/dataparcels-docs/src/docs/api/parcel/Parcel.md b/packages/dataparcels-docs/src/docs/api/parcel/Parcel.md index 6059ab2c..babafd03 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/Parcel.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/Parcel.md @@ -8,7 +8,7 @@ import IconParcel from 'content/parcel.gif'; {IconParcel} -Parcel is a data container. It's job is to hold your data, split it into smaller parts, and merge changes back together. +Parcel is a data container. Its job is to hold your data, split it into smaller parts, and merge changes back together. If you're using React, you probably won't be instanciating parcels directly. Please see the getting started page to see how to best use Parcels in a React app. @@ -20,9 +20,7 @@ import Parcel from 'react-dataparcels'; ```flow new Parcel({ value?: any, - handleChange?: Function, - // debugging options - debugRender?: boolean + handleChange?: Function }); ``` @@ -32,8 +30,6 @@ new Parcel({ The `handleChange` function will be called whenever the Parcel's value has been triggered to change. It is passed `newParcel`, a replacement Parcel containing the changes; and `changeRequest`, a ChangeRequest that contains details about the change itself. In `handleChange` you would typically implement logic to replace your current parcel with `newParcel`, but if you're using React you should read getting started to save you the trouble of implementing this yourself. -* - For debugging purposes. When set to `true` this causes all downstream ParcelBoundarys to display when they are being rendered and re-rendered. ```js // creates a Parcel that contains a value of 123 diff --git a/packages/dataparcels-docs/src/docs/api/parcel/ParcelAfter.md b/packages/dataparcels-docs/src/docs/api/parcel/ParcelAfter.md index 037fcdc7..798acf09 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/ParcelAfter.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/ParcelAfter.md @@ -1,5 +1,5 @@ -import Examples from 'content/Examples.md'; +import Link from 'gatsby-link'; ### Examples - +Many examples can be found on the data editing page. diff --git a/packages/dataparcels-docs/src/docs/api/parcel/batch.md b/packages/dataparcels-docs/src/docs/api/parcel/batch.md deleted file mode 100644 index 08740905..00000000 --- a/packages/dataparcels-docs/src/docs/api/parcel/batch.md +++ /dev/null @@ -1,3 +0,0 @@ -```flow -batch(batcher: Function): void -``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/batchAndReturn.md b/packages/dataparcels-docs/src/docs/api/parcel/batchAndReturn.md deleted file mode 100644 index fc712baa..00000000 --- a/packages/dataparcels-docs/src/docs/api/parcel/batchAndReturn.md +++ /dev/null @@ -1,3 +0,0 @@ -```flow -batchAndReturn(batcher: Function): ?Parcel // only on TopLevelParcels -``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/children.md b/packages/dataparcels-docs/src/docs/api/parcel/children.md new file mode 100644 index 00000000..0e1e34cd --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcel/children.md @@ -0,0 +1,29 @@ +```flow +children(mapper?: ParcelMapper): ParentType // only on ParentParcels + +type ParcelMapper = ( + item: Parcel, + property: string|number, + parent: Parcel +) => any; +``` + +Returns all of the Parcel's children as new ChildParcels, contained within the original Parcel's data structure. + +An optional `mapper` function can be passed, which will be called on each child. + +```js +let value = { + abc: 123, + def: 456 +}; + +let parcel = new Parcel({value}); +parcel.children(); + +// returns { +// abc: Parcel, // contains a value of 123 +// def: Parcel // contains a value of 456 +// } + +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/delete.md b/packages/dataparcels-docs/src/docs/api/parcel/delete.md index 135f718c..c03d6b83 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/delete.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/delete.md @@ -1,4 +1,25 @@ +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + ```flow -delete(): void +delete(): void // only on ChildParcels delete(key: string|number): void // only on ParentParcels, will delete a child ``` + +Calling `delete()` with no arguments will trigger a change that will delete the current Parcel off of its parent. This variation of the `delete()` method only exists on ChildParcels. + +On ParentParcels this method can be called with a `key`, which deletes the child value at that key. + +```js +let value = { + abc: 123, + def: 456 +}; +let parcel = new Parcel({value}); +parcel.get('abc').delete(); +// this triggers a change that sets the parcel's value to {def: 456} + +parcel.delete('abc'); +// this also triggers a change that sets the parcel's value to {def: 456} +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/deleteIn.md b/packages/dataparcels-docs/src/docs/api/parcel/deleteIn.md index ca08ddb9..9372e7e3 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/deleteIn.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/deleteIn.md @@ -1,3 +1,20 @@ +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + ```flow deleteIn(keyPath: Array): void // only on ParentParcels ``` + +Calling `setIn()` will trigger a change that will delete the value at the provided `keyPath`. + +```js +let value = { + a: { + b: 123 + } +}; +let parcel = new Parcel({value}); +parcel.deleteIn(['a','b']); +// this triggers a change that sets the parcel's value to {a: {}} +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/dispatch.md b/packages/dataparcels-docs/src/docs/api/parcel/dispatch.md index e9f93551..a16e372c 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/dispatch.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/dispatch.md @@ -1,3 +1,8 @@ +import Link from 'component/Link'; +import {Box, Link as HtmlLink, Message} from 'dcme-style'; + ```flow dispatch(dispatchable: Action|Action[]|ChangeRequest): void ``` + +The `dispatch()` method is used by Parcels internally to pass a ChangeRequest upward to the next Parcel in the chain. diff --git a/packages/dataparcels-docs/src/docs/api/parcel/get.md b/packages/dataparcels-docs/src/docs/api/parcel/get.md index 1ce5dfb9..704b163c 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/get.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/get.md @@ -23,10 +23,6 @@ parcel.get('xyz', 789).value; // returns 789 #### get() with indexed values -When called on a Parcel with an indexed value, such as an array, `get()` can accept an `index` or a `key`. -- `index` (number) is used to get a value based off its position. It can also be negative, indicating an offset from the end of the sequence. -- `key` (string) is used to get a specific value by its unique key within the Parcel. - ```js diff --git a/packages/dataparcels-docs/src/docs/api/parcel/getIn.md b/packages/dataparcels-docs/src/docs/api/parcel/getIn.md index f45a0a79..00ed77e0 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/getIn.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/getIn.md @@ -1,3 +1,5 @@ +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + ```flow getIn(keyPath: Array): Parcel // only on ParentParcels getIn(keyPath: Array, notSetValue: any): Parcel // only on ParentParcels @@ -15,11 +17,9 @@ let value = { } }; let parcel = new Parcel({value}); -parcel.get(['a','b']).value; // returns 123 -parcel.get(['a','z']).value; // returns undefined -parcel.get(['a','z'], 789).value; // returns 789 +parcel.getIn(['a','b']).value; // returns 123 +parcel.getIn(['a','z']).value; // returns undefined +parcel.getIn(['a','z'], 789).value; // returns 789 ``` -#### getIn() with indexed values - -... + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/has.md b/packages/dataparcels-docs/src/docs/api/parcel/has.md index 86e1e119..f610c61a 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/has.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/has.md @@ -2,4 +2,4 @@ has(key: string|number): boolean // only on ParentParcels ``` -Returns true if the parcel has a child at the provided `key` or `index`, or false otherwise. +Returns true if the Parcel has a child at the provided `key` or `index`, or false otherwise. diff --git a/packages/dataparcels-docs/src/docs/api/parcel/hasDispatched.md b/packages/dataparcels-docs/src/docs/api/parcel/hasDispatched.md deleted file mode 100644 index f4691653..00000000 --- a/packages/dataparcels-docs/src/docs/api/parcel/hasDispatched.md +++ /dev/null @@ -1,3 +0,0 @@ -```flow -hasDispatched(): boolean -``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/initialMeta.md b/packages/dataparcels-docs/src/docs/api/parcel/initialMeta.md index 23c6925b..1797abf5 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/initialMeta.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/initialMeta.md @@ -1,3 +1,20 @@ ```flow -initialMeta(initialMeta: Object): void +initialMeta(initialMeta: Object): Parcel +``` + +Parcel `meta` defaults to an empty object. The `initialMeta` method replaces this `meta` for all descendant Parcels. + +Once a descendant Parcel triggers a change, the initial meta is also propagated up to the top level Parcel. + +```js +let parcel = new Parcel({ + value: "abc" +}); + +parcel + .initialMeta({ + abc: 123 + }) + .meta // this returns {abc: 123} initially, but this can change after subsequent calls to setMeta() + ``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/insertAfter.md b/packages/dataparcels-docs/src/docs/api/parcel/insertAfter.md index d6b3369a..00ee8d27 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/insertAfter.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/insertAfter.md @@ -1,4 +1,22 @@ +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + ```flow -insertAfter(value: *): void // only on ElementParcels, will insert after self -insertAfter(key: string|number, value: *): void // only on IndexedParcels, will insert after child +insertAfter(value: any): void // only on ElementParcels, will insert after self +insertAfter(key: string|number, value: any): void // only on IndexedParcels, will insert after child ``` + +When called with one argument, this inserts `value` after the current Parcel, within the current ParentParcel. + +When called with two arguments, this inserts `value` after the child Parcel at `key`. + +```js +let value = ['a','b','c']; +let parcel = new Parcel({value}); +parcel.get(1).insertAfter('!'); +// this triggers a change that sets the parcel's value to ['a','b','!','c']; + +parcel.insertAfter(1, '!'); +// this also triggers a change that sets the parcel's value to ['a','b','!','c']; +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/insertBefore.md b/packages/dataparcels-docs/src/docs/api/parcel/insertBefore.md index 5e788267..d474a940 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/insertBefore.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/insertBefore.md @@ -1,4 +1,21 @@ +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + ```flow -insertBefore(value: *): void // only on ElementParcels, will insert before self -insertBefore(key: string|number, value: *): void // only on IndexedParcels, will insert before child +insertBefore(value: any): void // only on ElementParcels, will insert before self +insertBefore(key: string|number, value: any): void // only on IndexedParcels, will insert before child ``` + +When called with one argument, this inserts `value` before the current Parcel, within the current ParentParcel. +When called with two arguments, this inserts `value` as the next sibling Parcel before the child Parcel at `key`. + +```js +let value = ['a','b','c']; +let parcel = new Parcel({value}); +parcel.get(1).insertBefore('!'); +// this triggers a change that sets the parcel's value to ['a','!','b','c']; + +parcel.insertBefore(1, '!'); +// this also triggers a change that sets the parcel's value to ['a','!','b','c']; +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/key.md b/packages/dataparcels-docs/src/docs/api/parcel/key.md index 85f12434..45e53fd0 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/key.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/key.md @@ -4,9 +4,9 @@ import Link from 'component/Link'; key: string ``` -Returns the Parcel's `key`. Parcels automatically gives unique keys to all children of a parent parcel. See parcel keys for more info. +Returns the Parcel's `key`. Dataparcels automatically gives unique keys to all children of a parent parcel. See parcel keys for more info. -Because they are unique, the can be used as keys when rendering an array of elements with React. This is demonstrated in the Editing Arrays example. +Because they are unique, the can be used as keys when rendering an array of elements with React. This is demonstrated here. ```js let value = { diff --git a/packages/dataparcels-docs/src/docs/api/parcel/matchPipe.md b/packages/dataparcels-docs/src/docs/api/parcel/matchPipe.md deleted file mode 100644 index 671697c8..00000000 --- a/packages/dataparcels-docs/src/docs/api/parcel/matchPipe.md +++ /dev/null @@ -1,3 +0,0 @@ -```flow -matchPipe(match: string, ...updaters: Function[]): Parcel -``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/modifyChangeBatch.md b/packages/dataparcels-docs/src/docs/api/parcel/modifyChangeBatch.md deleted file mode 100644 index 9da5d2b0..00000000 --- a/packages/dataparcels-docs/src/docs/api/parcel/modifyChangeBatch.md +++ /dev/null @@ -1,3 +0,0 @@ -```flow -modifyChangeBatch(batcher: Function): void -``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/modifyChangeValue.md b/packages/dataparcels-docs/src/docs/api/parcel/modifyChangeValue.md deleted file mode 100644 index 8d1dfb0e..00000000 --- a/packages/dataparcels-docs/src/docs/api/parcel/modifyChangeValue.md +++ /dev/null @@ -1,4 +0,0 @@ -```flow -modifyChangeValue(updater: Function): void -``` - diff --git a/packages/dataparcels-docs/src/docs/api/parcel/modifyDown.md b/packages/dataparcels-docs/src/docs/api/parcel/modifyDown.md new file mode 100644 index 00000000..4470b098 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcel/modifyDown.md @@ -0,0 +1,25 @@ +import ValueUpdater from 'docs/notes/ValueUpdater.md'; + +```flow +modifyDown(updater: ValueUpdater): Parcel + +type ValueUpdater = (value: any, self: Parcel) => any; +``` + +`modifyDown` lets you modify a Parcel's value so that lower Parcels receive and make changes against the modified value. + +```js +let parcel = new Parcel({ + value: "abc" +}); + +parcel + .modifyDown(value => value.toUpperCase()); + .value // "ABC" (top level Parcel is still "abc") +``` + +It does not trigger any changes of its own, it just modifies the values that pass through it from above. Changes fired from beneath are passed through unchanged. + +The modify methods are particularly useful when your Parcel contains data you want to be able to make an editor for, but the data isn't stored in a format that allows you to do that easily. The `modifyDown()` and `modifyUp()` methods are often used with one another to make a value editable on the way down, and turn it back on the way up. + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/modifyUp.md b/packages/dataparcels-docs/src/docs/api/parcel/modifyUp.md new file mode 100644 index 00000000..75e0e782 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcel/modifyUp.md @@ -0,0 +1,52 @@ +import ValueUpdater from 'docs/notes/ValueUpdater.md'; + +```flow +modifyUp(updater: ValueUpdater): Parcel + +type ValueUpdater = (value: any, self: Parcel) => any; +``` + +`modifyUp()` lets you modify a Parcel's new value when a change is being propagated upward. + +```js +let parcel = new Parcel({ + value: "abc" +}); + +parcel + .modifyUp(value => value.toUpperCase()); + .set("def"); + +// this triggers a change to set the value to "def" +// which propagates upward through .modifyUp() +// .modifyUp() turns "def" into "DEF" +// and the change request continues up to the original Parcel + +// The Parcel then has a new value of "DEF" +``` + +It does not trigger any changes of its own, but awaits a change from below. Values from above are passed through unchanged. + +The modify methods are particularly useful when your Parcel contains data you want to be able to make an editor for, but the data isn't stored in a format that allows you to do that easily. The `modifyDown()` and `modifyUp()` methods are often used with one another to make a value editable on the way down, and turn it back on the way up. + +#### Cancelling a change + +You can also cancel a change by returning `CancelActionMarker` from `modifyUp()`'s updater. This allows you to programatically prevent certain changes from being applied to the data in the top level Parcel. This example shows an input that cancels any changes that would set the value to `null`: + +```js +import CancelActionMarker from 'dataparcels/CancelActionMarker'; +// or +import CancelActionMarker from 'react-dataparcels/CancelActionMarker'; + +let parcel = new Parcel({ + value: 123 +}) + +parcel = parcel.modifyUp(value => value === null ? CancelActionMarker : value); + +parcel.set(456); // this would work, value becomes 123 +parcel.set(null); // this would cause no change + +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/modifyValue.md b/packages/dataparcels-docs/src/docs/api/parcel/modifyValue.md deleted file mode 100644 index ad1086e3..00000000 --- a/packages/dataparcels-docs/src/docs/api/parcel/modifyValue.md +++ /dev/null @@ -1,3 +0,0 @@ -```flow -modifyValue(updater: Function): void -``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/move.md b/packages/dataparcels-docs/src/docs/api/parcel/move.md new file mode 100644 index 00000000..b80ed0a2 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcel/move.md @@ -0,0 +1,23 @@ +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + +```flow +move(key: string|number): void // only on ElementParcels, will move with sibling +move(keyA: string|number, keyB: string|number): void // only on IndexedParcels, will move children +``` + +When called with one argument, this moves the current Parcel to the position of `key`, within the current ParentParcel. +Other elements will shift left to fill any gaps left and/or shift right to make room for moved values. + +When called with two arguments, this moves the child Parcel at `keyA` to the position of `keyB`. + +```js +let value = ['a','b','c']; +let parcel = new Parcel({value}); +parcel.get(2).move(0); +// this triggers a change that sets the parcel's value to ['c','a','b']; + +parcel.move(2, 0); +// this also triggers a change that sets the parcel's value to ['c','a','b']; +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/onChange.md b/packages/dataparcels-docs/src/docs/api/parcel/onChange.md index ee44497b..5c051a4e 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/onChange.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/onChange.md @@ -1,7 +1,7 @@ import {Box, Link, Message, Text} from 'dcme-style'; ```flow -onChange(value: *): void +onChange(value: any): void ``` This is designed for use with input components that call `onChange` with a new value. @@ -9,7 +9,7 @@ It triggers a change that replaces the current value in the Parcel with the `val If called on a ParentParcel, any child information that the Parcel had is removed, such as child keys or meta. -It is equivalent to calling set with no key. +It is equivalent to calling set() with no key. ```js let parcel = new Parcel({ diff --git a/packages/dataparcels-docs/src/docs/api/parcel/onChangeDOM.md b/packages/dataparcels-docs/src/docs/api/parcel/onChangeDOM.md index 308f3ed2..9ef83b6e 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/onChangeDOM.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/onChangeDOM.md @@ -1,7 +1,7 @@ import {Box, Link, Message, Text} from 'dcme-style'; ```flow -onChangeDOM(event: Event): void +onChangeDOM(event: HTMLEvent): void ``` This is designed for use with HTML inputs. diff --git a/packages/dataparcels-docs/src/docs/api/parcel/ping.md b/packages/dataparcels-docs/src/docs/api/parcel/ping.md deleted file mode 100644 index dd69f228..00000000 --- a/packages/dataparcels-docs/src/docs/api/parcel/ping.md +++ /dev/null @@ -1,3 +0,0 @@ -```flow -ping(): void -``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/pipe.md b/packages/dataparcels-docs/src/docs/api/parcel/pipe.md index f5842205..c38f899a 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/pipe.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/pipe.md @@ -5,8 +5,8 @@ pipe(...updaters: Function[]): Parcel The `pipe` method allows for a parcel to be passed through one or more parcel modifying functions, while retaining the ability to chain. It allows for easier function composition. ```js -let valueToString = (parcel) => parcel.modifyValue(value => `${value}`); -let changeToNumber = (parcel) => parcel.modifyChangeValue(value => Number(value)); +let valueToString = (parcel) => parcel.modifyDown(value => `${value}`); +let changeToNumber = (parcel) => parcel.modifyUp(value => Number(value)); let parcel = new Parcel({value: 123}); parcel @@ -22,7 +22,7 @@ The above is equivalent to: ```js let parcel = new Parcel({value: 123}); parcel - .modifyValue(value => `${value}`) - .modifyChangeValue(value => Number(value)) + .modifyDown(value => `${value}`) + .modifyUp(value => Number(value)) .value // returns "123" ``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/pop.md b/packages/dataparcels-docs/src/docs/api/parcel/pop.md index daca3ead..645b98a5 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/pop.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/pop.md @@ -1,3 +1,12 @@ ```flow pop(): void // only on IndexedParcels ``` + +This triggers a change that pops the last value off of the end of the current ParentParcel. + +```js +let value = ['a','b','c']; +let parcel = new Parcel({value}); +parcel.pop(); +// this triggers a change that sets the parcel's value to ['a','b']; +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/push.md b/packages/dataparcels-docs/src/docs/api/parcel/push.md index c61a8ed4..6485fc3a 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/push.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/push.md @@ -1,3 +1,12 @@ ```flow -push(value: *): void // only on IndexedParcels +push(...values: Array<*>): void // only on IndexedParcels +``` + +This triggers a change that pushes all provided values to the end of the current ParentParcel. + +```js +let value = ['a','b','c']; +let parcel = new Parcel({value}); +parcel.push('d','e'); +// this triggers a change that sets the parcel's value to ['a','b','c','d','e']; ``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/set.md b/packages/dataparcels-docs/src/docs/api/parcel/set.md index 9db7e179..12cadc8d 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/set.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/set.md @@ -1,4 +1,32 @@ +import {Box, Link, Message} from 'dcme-style'; +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + ```flow -set(value: *): void -set(key: string|number, value: *): void // only on ParentParcels, will set a child +set(value: any): void +set(key: string|number, value: any): void // only on ParentParcels, will set a child ``` + +Calling `set()` with one argument will trigger a change that replaces the current value in the Parcel with the `value` provided. This is equivalent to calling onChange(). + +```js +let parcel = new Parcel({ + value: 123 +}); +parcel.set(456); +// this triggers a change that sets the parcel's value to 456 +``` + +On ParentParcels this method can also be called with a `key`, which sets the child value at that key. + +```js +let parcel = new Parcel({ + value: { + abc: 123, + def: 789 + } +}); +parcel.set('abc', 456); +// this triggers a change that sets the parcel's value to {abc: 456, def: 789} +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/setChangeRequestMeta.md b/packages/dataparcels-docs/src/docs/api/parcel/setChangeRequestMeta.md deleted file mode 100644 index 6ac69e22..00000000 --- a/packages/dataparcels-docs/src/docs/api/parcel/setChangeRequestMeta.md +++ /dev/null @@ -1,3 +0,0 @@ -```flow -setChangeRequestMeta(meta: Object): void -``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/setIn.md b/packages/dataparcels-docs/src/docs/api/parcel/setIn.md index dfee5742..5a4a593b 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/setIn.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/setIn.md @@ -1,3 +1,21 @@ +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + ```flow -setIn(keyPath: Array, value: *): void // only on ParentParcels +setIn(keyPath: Array, value: any): void // only on ParentParcels ``` + +Calling `setIn()` will trigger a change that will set the value at the provided `keyPath`. + +```js +let value = { + a: { + b: 123, + c: 789 + } +}; +let parcel = new Parcel({value}); +parcel.setIn(['a','b'], 456); +// this triggers a change that sets the parcel's value to {a: {b: 456, c: 789}} +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/setMeta.md b/packages/dataparcels-docs/src/docs/api/parcel/setMeta.md index 662f84bb..4084d411 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/setMeta.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/setMeta.md @@ -1,3 +1,26 @@ +import ParcelMeta from 'docs/notes/ParcelMeta.md'; +import Link from 'component/Link'; + ```flow setMeta(partialMeta: Object): void ``` + +Triggers a change that sets `meta` at the current parcel's location. Values on the `partialMeta` object are merged shallowly onto any existing `meta`. + + + +```js +let parcel = new Parcel({ + value: "abc" +}); + +parcel.setMeta({ + abc: 123 +}); +// ^ this triggers a change that sets the parcel's meta to {abc: 123} + +parcel.setMeta({ + def: 456 +}); +// ^ this triggers a change that sets the parcel's meta to {abc: 123, def: 456} +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/shift.md b/packages/dataparcels-docs/src/docs/api/parcel/shift.md index 143be743..784e7e89 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/shift.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/shift.md @@ -1,3 +1,12 @@ ```flow shift(): void // only on IndexedParcels ``` + +This triggers a change that pops the first value off of the start of the current ParentParcel. + +```js +let value = ['a','b','c']; +let parcel = new Parcel({value}); +parcel.shift(); +// this triggers a change that sets the parcel's value to ['b','c']; +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/size.md b/packages/dataparcels-docs/src/docs/api/parcel/size.md index 3b56d840..6dc597ca 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/size.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/size.md @@ -2,4 +2,4 @@ size(): number // only on ParentParcels ``` -Returns the number of children this parcel has. +Returns the number of children this Parcel has. diff --git a/packages/dataparcels-docs/src/docs/api/parcel/spread.md b/packages/dataparcels-docs/src/docs/api/parcel/spread.md index cdaca7c7..879368c5 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/spread.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/spread.md @@ -1,6 +1,8 @@ ```flow -spread(): {value: *, onChange: Function} -spread(notFoundValue: any): {value: *, onChange: Function} +spread(): {value: *, onChange: OnChangeFunction} +spread(notFoundValue: any): {value: *, onChange: OnChangeFunction} + +type OnChangeFunction = (value: any) => void; ``` Returns an object with the Parcel's value and its onChange function. diff --git a/packages/dataparcels-docs/src/docs/api/parcel/spreadDOM.md b/packages/dataparcels-docs/src/docs/api/parcel/spreadDOM.md index 2a6d3b0a..88f86a6d 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/spreadDOM.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/spreadDOM.md @@ -1,6 +1,8 @@ ```flow -spreadDOM(): {value: *, onChange: Function} -spreadDOM(notFoundValue: any): {value: *, onChange: Function} +spreadDOM(): {value: *, onChange: OnChangeDOMFunction} +spreadDOM(notFoundValue: any): {value: *, onChange: OnChangeDOMFunction} + +type OnChangeDOMFunction = (event: HTMLEvent) => void; ``` Returns an object with the Parcel's value and its onChangeDOM function. diff --git a/packages/dataparcels-docs/src/docs/api/parcel/swap.md b/packages/dataparcels-docs/src/docs/api/parcel/swap.md index 853d6e4d..3693a7a6 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/swap.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/swap.md @@ -1,4 +1,22 @@ +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + ```flow swap(key: string|number): void // only on ElementParcels, will swap with sibling swap(keyA: string|number, keyB: string|number): void // only on IndexedParcels, will swap children ``` + +When called with one argument, this swaps the current Parcel with the one at the position of `key`, within the current ParentParcel. + +When called with two arguments, this swaps the child Parcel at `keyA` with the one at the position of `keyB`. + +```js +let value = ['a','b','c']; +let parcel = new Parcel({value}); +parcel.get(0).swap(1); +// this triggers a change that sets the parcel's value to ['b','a','c']; + +parcel.swap(0, 1); +// this also triggers a change that sets the parcel's value to ['b','a','c']; +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/swapNext.md b/packages/dataparcels-docs/src/docs/api/parcel/swapNext.md index c18f553a..950f5154 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/swapNext.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/swapNext.md @@ -1,4 +1,22 @@ +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + ```flow swapNext(): void // only on ElementParcels, will swap with next sibling swapNext(key: string|number): void // only on IndexedParcels, will swap child with next child ``` + +When called with no arguments, this swaps the current Parcel with its next sibling Parcel, within the current ParentParcel. If called on the last child, it swaps with the first child. + +When called with one argument, this swaps the child at the position of `key` with the next sibling Parcel. + +```js +let value = ['a','b','c']; +let parcel = new Parcel({value}); +parcel.get(0).swapNext(); +// this triggers a change that sets the parcel's value to ['b','a','c']; + +parcel.swapNext(0); +// this also triggers a change that sets the parcel's value to ['b','a','c']; +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/swapPrev.md b/packages/dataparcels-docs/src/docs/api/parcel/swapPrev.md index 61155893..36100b8a 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/swapPrev.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/swapPrev.md @@ -1,4 +1,22 @@ +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + ```flow swapPrev(): void // only on ElementParcels, will swap with previous sibling swapPrev(key: string|number): void // only on IndexedParcels, will swap child with previous child ``` + +When called with no arguments, this swaps the current Parcel with its previous sibling Parcel, within the current ParentParcel. If called on the first child, it swaps with the last child. + +When called with one argument, this swaps the child at the position of `key` with the previous sibling Parcel. + +```js +let value = ['a','b','c']; +let parcel = new Parcel({value}); +parcel.get(1).swapPrev(); +// this triggers a change that sets the parcel's value to ['b','a','c']; + +parcel.swapPrev(1); +// this also triggers a change that sets the parcel's value to ['b','a','c']; +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/toArray.md b/packages/dataparcels-docs/src/docs/api/parcel/toArray.md index ed5fe168..13a76110 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/toArray.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/toArray.md @@ -1,3 +1,29 @@ ```flow -toArray(mapper?: Function): Parcel[] // only on ParentParcels +toArray(mapper?: ParcelMapper): Array // only on ParentParcels + +type ParcelMapper = ( + item: Parcel, + property: string|number, + parent: Parcel +) => any; +``` + +Like [children()](#children), expect the returned data structure is cast to an array. + +An optional `mapper` function can be passed, which will be called on each child. + +```js +let value = { + abc: 123, + def: 456 +}; + +let parcel = new Parcel({value}); +parcel.toArray(); + +// returns [ +// Parcel, // contains a value of 123 +// Parcel // contains a value of 456 +// ] + ``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/toObject.md b/packages/dataparcels-docs/src/docs/api/parcel/toObject.md index 3b0ac152..6ba17398 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/toObject.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/toObject.md @@ -1,3 +1,29 @@ ```flow -toObject(mapper?: Function): Object // only on ParentParcels +toObject(mapper?: ParcelMapper): Object // only on ParentParcels + +type ParcelMapper = ( + item: Parcel, + property: string|number, + parent: Parcel +) => any; +``` + +Like [children()](#children), expect the returned data structure is cast to an object. + +An optional `mapper` function can be passed, which will be called on each child. + +```js +let value = { + abc: 123, + def: 456 +}; + +let parcel = new Parcel({value}); +parcel.toObject(); + +// returns { +// abc: Parcel, // contains a value of 123 +// def: Parcel // contains a value of 456 +// } + ``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/unshift.md b/packages/dataparcels-docs/src/docs/api/parcel/unshift.md index a896e692..549c7e91 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/unshift.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/unshift.md @@ -1,3 +1,12 @@ ```flow -unshift(value: *): void // only on IndexedParcels +unshift(...values: Array<*>): void // only on IndexedParcels +``` + +This triggers a change that unshifts all provided values to the start of the current ParentParcel. + +```js +let value = ['a','b','c']; +let parcel = new Parcel({value}); +parcel.unshift('d','e'); +// this triggers a change that sets the parcel's value to ['d','e','a','b','c']; ``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/update.md b/packages/dataparcels-docs/src/docs/api/parcel/update.md index def55433..68510fc5 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/update.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/update.md @@ -1,4 +1,33 @@ +import ValueUpdater from 'docs/notes/ValueUpdater.md'; + ```flow -update(updater: Function): void -update(key: string|number, updater: Function): void // only on ParentParcels, will set a child +update(updater: ValueUpdater): void +update(key: string|number, updater: ValueUpdater): void // only on ParentParcels, will update a child + +type ValueUpdater = (value: any, self: Parcel) => any; ``` + +Calling `update()` with one argument will trigger a change that replaces the current value in the Parcel with the result of the value updater provided to it. The value updater is passed the current value of the Parcel, from which you can return the intended replacement value. + +```js +let parcel = new Parcel({ + value: 123 +}); +parcel.update(value => value + 1); +// this triggers a change that sets the parcel's value to 124 +``` + +On ParentParcels this method can also be called with a `key`, which updates the child value at that key. The value updater is passed the current value found at the `key`, from which you can return the intended replacement value. + +```js +let parcel = new Parcel({ + value: { + abc: 123, + def: 789 + } +}); +parcel.update('abc', value => value + 1); +// this triggers a change that sets the parcel's value to {abc: 124, def: 789} +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/updateIn.md b/packages/dataparcels-docs/src/docs/api/parcel/updateIn.md index 0dadd4ff..fa4f4090 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/updateIn.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/updateIn.md @@ -1,3 +1,21 @@ +import ValueUpdater from 'docs/notes/ValueUpdater.md'; + ```flow -updateIn(keyPath: Array, updater: Function): void // only on ParentParcels +updateIn(keyPath: Array, updater: ValueUpdater): void // only on ParentParcels ``` + +Calling `updateIn()` will trigger a change that will update the value at the provided `keyPath`. The value updater is passed the current value found at the `keyPath`, from which you can return the intended replacement value. + +```js +let value = { + a: { + b: 123, + c: 789 + } +}; +let parcel = new Parcel({value}); +parcel.updateIn(['a','b'], value => value + 1); +// this triggers a change that sets the parcel's value to {a: {b: 124, c: 789}} +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/updateMeta.md b/packages/dataparcels-docs/src/docs/api/parcel/updateMeta.md deleted file mode 100644 index 364f9ab4..00000000 --- a/packages/dataparcels-docs/src/docs/api/parcel/updateMeta.md +++ /dev/null @@ -1,3 +0,0 @@ -```flow -updateMeta(updater: Function): void -``` diff --git a/packages/dataparcels-docs/src/docs/api/parcel/updateShape.md b/packages/dataparcels-docs/src/docs/api/parcel/updateShape.md new file mode 100644 index 00000000..6bbd042e --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcel/updateShape.md @@ -0,0 +1,14 @@ +import Link from 'component/Link'; +import ShapeUpdater from 'docs/notes/ShapeUpdater.md'; +import {Box, Link as HtmlLink, Message} from 'dcme-style'; + +```flow +updateShape(updater: ShapeUpdater): void +updateShape(key: string|number, updater: ShapeUpdater): void // only on ParentParcels, will set a child + +type ShapeUpdater = (parcelShape: ParcelShape) => any +``` + +The `updateShape()` method is an advanced version of update(). It provides a ParcelShape containing the value to update. It expects either a ParcelShape or a value to be returned. If a value is returned, and if the returned value has children, then those children *must* all be ParcelShapes. + + diff --git a/packages/dataparcels-docs/src/docs/api/parcel/value.md b/packages/dataparcels-docs/src/docs/api/parcel/value.md index 407d001a..12ed5f45 100644 --- a/packages/dataparcels-docs/src/docs/api/parcel/value.md +++ b/packages/dataparcels-docs/src/docs/api/parcel/value.md @@ -1,5 +1,3 @@ -import IndexedKeys from 'docs/notes/IndexedKeys.md'; - ```flow value: any ``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelBoundary/ParcelBoundary.md b/packages/dataparcels-docs/src/docs/api/parcelBoundary/ParcelBoundary.md index 6367e66c..ad256c75 100644 --- a/packages/dataparcels-docs/src/docs/api/parcelBoundary/ParcelBoundary.md +++ b/packages/dataparcels-docs/src/docs/api/parcelBoundary/ParcelBoundary.md @@ -11,14 +11,14 @@ import {Box, Message} from 'dcme-style'; {IconParcelBoundary} -ParcelBoundary is a React component. It's job is to optimise rendering performance, and to optionally control the flow of parcel changes. +ParcelBoundary is a React component. Its job is to optimise rendering performance, and to optionally control the flow of parcel changes. Each ParcelBoundary is passed a Parcel. By default the ParcelBoundary uses pure rendering, and will only update when the Parcel's data changes to avoid unnecessary re-rendering. ParcelBoundaries have an internal action buffer that can hold onto changes as they exit the boundary. These are normally released immediately, but also allow for debouncing changes, or putting a hold on all changes so they can be released later. ```js -import {ParcelBoundary} from 'react-dataparcels'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; ``` ```js diff --git a/packages/dataparcels-docs/src/docs/api/parcelBoundary/keepState.md b/packages/dataparcels-docs/src/docs/api/parcelBoundary/keepState.md new file mode 100644 index 00000000..3939942b --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelBoundary/keepState.md @@ -0,0 +1,31 @@ +import Link from 'component/Link'; + +```flow +keepState?: boolean = false // optional +``` + +The default behaviour of ParcelBoundary is to update its contents whenever the Parcel it receives via props has changed. + +When `keepState` is true, it ensures that any changes that originate from inside the ParcelBoundary are not overwritten by the incoming new props containing the updated value. This won't be a problem for most ParcelBoundaries, but it can be if modify methods are being used above the ParcelBoundary, and if those modify methods subtly change the value that ends up being passed back down into the ParcelBoundary. In this situation you may see inputs change more than what was typed into the input. + +The `keepState` option effectively makes the ParcelBoundary the master of its own state, and *not* the Parcel it receives via props. The one exception is if the change originates from outside the ParcelBoundary, in which case the ParcelBoundary will update its contents like normal. + +```js +let numberParcel = parcel + .modifyDown(number => `${number}`) + // ^ turn value into a string on the way down + .modifyUp(string => Number(string.replace(/[^0-9]/g, ""))); + // ^ turn value back into a number on the way up + +return + {(parcel) => } +; + +// without keepState, if you type "0.10" in the input above it would +// immediately be replaced with "0.1", as the new value is turned +// into a number on the way up, and into a string on the way down, +// which would make typing very frustrating. +// keepState keeps "0.10" in the text field. +``` + +Example diff --git a/packages/dataparcels-docs/src/docs/api/parcelBoundary/parcel.md b/packages/dataparcels-docs/src/docs/api/parcelBoundary/parcel.md index 9080e12c..b7b516af 100644 --- a/packages/dataparcels-docs/src/docs/api/parcelBoundary/parcel.md +++ b/packages/dataparcels-docs/src/docs/api/parcelBoundary/parcel.md @@ -18,4 +18,4 @@ The `parcel` can be accessed from inside the ParcelBoundary via the first argume ``` -Example +Example diff --git a/packages/dataparcels-docs/src/docs/api/parcelBoundaryHoc/ParcelBoundaryHoc.md b/packages/dataparcels-docs/src/docs/api/parcelBoundaryHoc/ParcelBoundaryHoc.md index d16c86a5..32126870 100644 --- a/packages/dataparcels-docs/src/docs/api/parcelBoundaryHoc/ParcelBoundaryHoc.md +++ b/packages/dataparcels-docs/src/docs/api/parcelBoundaryHoc/ParcelBoundaryHoc.md @@ -11,7 +11,7 @@ import {Box, Message} from 'dcme-style'; {IconParcelBoundaryHoc} -ParcelBoundaryHoc is a React higher order component. It's job is to control the flow of parcel changes. It is the higher order component version of a ParcelBoundary. +ParcelBoundaryHoc is a React higher order component. Its job is to control the flow of parcel changes. It is the higher order component version of a ParcelBoundary. Each ParcelBoundaryHoc is given a name, and expects that it will be given Parcel as a prop of the same name. @@ -20,7 +20,7 @@ ParcelBoundaryHocs have an internal action buffer that can hold onto changes as Unlike ParcelBoundary, it cannot use pure rendering. ```js -import {ParcelBoundaryHoc} from 'react-dataparcels'; +import ParcelBoundaryHoc from 'react-dataparcels/ParcelBoundaryHoc'; ``` ```js diff --git a/packages/dataparcels-docs/src/docs/api/parcelBoundaryHoc/name.md b/packages/dataparcels-docs/src/docs/api/parcelBoundaryHoc/name.md index f65966b5..1e8304cf 100644 --- a/packages/dataparcels-docs/src/docs/api/parcelBoundaryHoc/name.md +++ b/packages/dataparcels-docs/src/docs/api/parcelBoundaryHoc/name.md @@ -4,6 +4,6 @@ import Link from 'component/Link'; name: string | (props: *) => string ``` -The name of the prop that will contain a parcel. +The name of the prop that will contain a parcel. This should correspond with a prop containing a Parcel. The parcel is allowed to be undefined, in which case the ParcelBoundaryHoc will have no effect. diff --git a/packages/dataparcels-docs/src/docs/api/parcelHoc/ParcelHoc.md b/packages/dataparcels-docs/src/docs/api/parcelHoc/ParcelHoc.md index 28229343..39abeba8 100644 --- a/packages/dataparcels-docs/src/docs/api/parcelHoc/ParcelHoc.md +++ b/packages/dataparcels-docs/src/docs/api/parcelHoc/ParcelHoc.md @@ -10,12 +10,12 @@ import IconParcelHoc from 'content/parcelhoc.gif'; {IconParcelHoc} -ParcelHoc is a React higher order component. It's job is to provide a parcel as a prop, and to handle how the parcel binds to React props and lifecycle events. +ParcelHoc is a React higher order component. Its job is to provide a parcel as a prop, and to handle how the parcel binds to React props and lifecycle events. -It is recommended that you use ParcelHoc, rather than managing your own Parcel state. +It is recommended that you use ParcelHoc, rather than managing your own Parcel state. ```js -import {ParcelHoc} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; ``` ```flow @@ -28,7 +28,6 @@ ParcelHoc({ pipe?: Function // debugging options debugParcel?: boolean - debugRender?: boolean }); ``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelHoc/debugRender.md b/packages/dataparcels-docs/src/docs/api/parcelHoc/debugRender.md deleted file mode 100644 index 5db58cc1..00000000 --- a/packages/dataparcels-docs/src/docs/api/parcelHoc/debugRender.md +++ /dev/null @@ -1,7 +0,0 @@ -import Link from 'component/Link'; - -```flow -debugRender?: boolean = false // optional -``` - -For debugging purposes. When set to `true` this causes all downstream ParcelBoundarys to display when they are being rendered and re-rendered. diff --git a/packages/dataparcels-docs/src/docs/api/parcelHoc/onChange.md b/packages/dataparcels-docs/src/docs/api/parcelHoc/onChange.md index 160dcf8e..5f355d80 100644 --- a/packages/dataparcels-docs/src/docs/api/parcelHoc/onChange.md +++ b/packages/dataparcels-docs/src/docs/api/parcelHoc/onChange.md @@ -1,10 +1,10 @@ import Link from 'component/Link'; ```flow -onChange?: (props: Object) => (parcel: Parcel, changeRequest: ChangeRequest) => void // optional +onChange?: (props: Object) => (value: any, changeRequest: ChangeRequest) => void // optional ``` -The `onChange` function is called whenever ParcelHoc changes. It expects to be given a double barrel function. The first function will be passed `props`, and the next is passed the recently-changed Parcel. It is only fired if the value actually changes. +The `onChange` function is called whenever ParcelHoc changes. It expects to be given a double barrel function. The first function will be passed `props`, and the next is passed the new value. It is only fired if the value actually changes. `onChange` is often used to relay changes further up the React DOM heirarchy. This works in a very similar way to [uncontrolled components in React](https://reactjs.org/docs/uncontrolled-components.html). diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/ParcelShape.md b/packages/dataparcels-docs/src/docs/api/parcelShape/ParcelShape.md new file mode 100644 index 00000000..90e436eb --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/ParcelShape.md @@ -0,0 +1,37 @@ +import Link from 'component/Link'; +import Param from 'component/Param'; +import ApiPageIcon from 'component/ApiPageIcon'; +import ParcelCreateReact from 'docs/notes/ParcelCreateReact.md'; +import IconParcel from 'content/parcelshape.gif'; + +# ParcelShape + +{IconParcel} + +ParcelShape is a data container very similar to a Parcel but without the automatic data binding. All it does is contain data, no strings attached, and provide methods for you to alter its data. + +These exist to be used with updater methods, such as Parcel.modifyDown, to provide a safe way to alter the shape of data in a Parcel. +ParcelShape's methods are a subset of Parcel's methods. + +```js +import ParcelShape from 'dataparcels/ParcelShape'; +import ParcelShape from 'react-dataparcels/ParcelShape'; +``` + +```flow +new ParcelShape(value?: any); +``` + +* + The value you want to put in the ParcelShape. This value will be changed immutably when change methods are called on the ParcelShape. The data type of the `value` will determine the type of ParcelShape that will be created, and will determine which methods you can use to change the value. Please read Parcel types for more info. + +```js +// creates a Parcel that contains a value of 123 +let parcelShape = new ParcelShape(123); +``` + +## Example Usage + +ParcelShapes are used in a very similar way to [Immutable.js Maps and Lists](https://facebook.github.io/immutable-js/docs/), by calling methods that return new and updated ParcelShapes. + +TODOTODO diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/children.md b/packages/dataparcels-docs/src/docs/api/parcelShape/children.md new file mode 100644 index 00000000..51bac7bb --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/children.md @@ -0,0 +1,21 @@ +```flow +children(): ParentType // only on ParentParcels +``` + +Returns all of the ParcelShape's children as new ParcelShapes, contained within the original ParcelShape's data structure. + +```js +let value = { + abc: 123, + def: 456 +}; + +let parcelShape = new ParcelShape(value); +parcelShape.children(); + +// returns { +// abc: ParcelShape, // contains a value of 123 +// def: ParcelShape // contains a value of 456 +// } + +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/data.md b/packages/dataparcels-docs/src/docs/api/parcelShape/data.md new file mode 100644 index 00000000..2a2e9eed --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/data.md @@ -0,0 +1,11 @@ +import Link from 'component/Link'; + +```flow +data: Object +``` + +Returns an object containing the ParcelShape's data, which includes: +* `value` - The ParcelShape's value +* `meta` - The ParcelShape's meta object +* `key` - The ParcelShape's key +* `child` - The ParcelShape's child information, which includes any `meta`, `key` and `child` data related to the `value`s children. diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/delete.md b/packages/dataparcels-docs/src/docs/api/parcelShape/delete.md new file mode 100644 index 00000000..a0f76966 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/delete.md @@ -0,0 +1,4 @@ +```flow +delete(): ParcelShape +delete(key: string|number): ParcelShape // only on ParentParcels, will delete a child +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/deleteIn.md b/packages/dataparcels-docs/src/docs/api/parcelShape/deleteIn.md new file mode 100644 index 00000000..4aba0bfa --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/deleteIn.md @@ -0,0 +1,3 @@ +```flow +deleteIn(keyPath: Array): ParcelShape // only on ParentParcels +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/get.md b/packages/dataparcels-docs/src/docs/api/parcelShape/get.md new file mode 100644 index 00000000..cdd6630f --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/get.md @@ -0,0 +1,34 @@ +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + +```flow +get(key: string|number): ParcelShape // only on ParentParcels +get(key: string|number, notSetValue: any): ParcelShape // only on ParentParcels +``` + +Returns a ParcelShape containing the value associated with the provided key / index. +If the key / index doesn't exist, a ParcelShape with a value of `notSetValue` will be returned. +If `notSetValue` is not provided then a ParcelShape with a value of + `undefined` will be returned. + +```js +let value = { + abc: 123, + def: 456 +}; +let parcelShape = new ParcelShape(value); +parcelShape.get('abc').value; // returns 123 +parcelShape.get('xyz').value; // returns undefined +parcelShape.get('xyz', 789).value; // returns 789 +``` + +#### get() with indexed values + + + +```js +let value = ['abc', 'def', 'ghi']; +let parcelShape = new ParcelShape(value); +parcelShape.get(0).value; // returns 'abc' +parcelShape.get(-1).value; // returns 'ghi' +parcelShape.get('#a').value; // returns 'abc' +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/getIn.md b/packages/dataparcels-docs/src/docs/api/parcelShape/getIn.md new file mode 100644 index 00000000..022e7299 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/getIn.md @@ -0,0 +1,25 @@ +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + +```flow +getIn(keyPath: Array): ParcelShape // only on ParentParcels +getIn(keyPath: Array, notSetValue: any): ParcelShape // only on ParentParcels +``` + +Returns a ParcelShape containing the value associated with the provided key path. +If the key path doesn't exist, a ParcelShape with a value of `notSetValue` will be returned. +If `notSetValue` is not provided then a ParcelShape with a value of + `undefined` will be returned. + +```js +let value = { + a: { + b: 123 + } +}; +let parcelShape = new ParcelShape(value); +parcelShape.get(['a','b']).value; // returns 123 +parcelShape.get(['a','z']).value; // returns undefined +parcelShape.get(['a','z'], 789).value; // returns 789 +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/has.md b/packages/dataparcels-docs/src/docs/api/parcelShape/has.md new file mode 100644 index 00000000..a2b0990b --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/has.md @@ -0,0 +1,5 @@ +```flow +has(key: string|number): boolean // only on ParentParcels +``` + +Returns true if the ParcelShape has a child at the provided `key` or `index`, or false otherwise. diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/insertAfter.md b/packages/dataparcels-docs/src/docs/api/parcelShape/insertAfter.md new file mode 100644 index 00000000..b68f8177 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/insertAfter.md @@ -0,0 +1,4 @@ +```flow +insertAfter(value: *): ParcelShape // only on ElementParcels, will insert after self +insertAfter(key: string|number, value: *): ParcelShape // only on IndexedParcels, will insert after child +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/insertBefore.md b/packages/dataparcels-docs/src/docs/api/parcelShape/insertBefore.md new file mode 100644 index 00000000..7710d2f3 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/insertBefore.md @@ -0,0 +1,4 @@ +```flow +insertBefore(value: *): ParcelShape // only on ElementParcels, will insert before self +insertBefore(key: string|number, value: *): ParcelShape // only on IndexedParcels, will insert before child +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/isChild.md b/packages/dataparcels-docs/src/docs/api/parcelShape/isChild.md new file mode 100644 index 00000000..3a7c4e6d --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/isChild.md @@ -0,0 +1,9 @@ +import Link from 'component/Link'; + +```flow +isChild(): boolean +``` + +Returns true if the ParcelShape is a child parcel. Read Parcel types for more info. + +When a ParcelShape is a child parcel, it allows the use of child methods. diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/isElement.md b/packages/dataparcels-docs/src/docs/api/parcelShape/isElement.md new file mode 100644 index 00000000..6f8ca285 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/isElement.md @@ -0,0 +1,9 @@ +import Link from 'component/Link'; + +```flow +isElement(): boolean +``` + +Returns true if the ParcelShape is an element parcel. Read Parcel types for more info. + +When a ParcelShape is an element parcel, it allows the use of element methods. diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/isIndexed.md b/packages/dataparcels-docs/src/docs/api/parcelShape/isIndexed.md new file mode 100644 index 00000000..435fd902 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/isIndexed.md @@ -0,0 +1,9 @@ +import Link from 'component/Link'; + +```flow +isIndexed(): boolean +``` + +Returns true if the ParcelShape is an indexed parcel. Read Parcel types for more info. + +When a ParcelShape is an indexed parcel, it allows the use of indexed methods. diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/isParent.md b/packages/dataparcels-docs/src/docs/api/parcelShape/isParent.md new file mode 100644 index 00000000..7056835a --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/isParent.md @@ -0,0 +1,9 @@ +import Link from 'component/Link'; + +```flow +isParent(): boolean +``` + +Returns true if the ParcelShape is a parent parcel. Read Parcel types for more info. + +When a ParcelShape is a parent parcel, it allows the use of parent methods. diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/isTopLevel.md b/packages/dataparcels-docs/src/docs/api/parcelShape/isTopLevel.md new file mode 100644 index 00000000..f01d0258 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/isTopLevel.md @@ -0,0 +1,7 @@ +import Link from 'component/Link'; + +```flow +isTopLevel(): boolean +``` + +Returns true if the ParcelShape is a top level parcel. Read Parcel types for more info. diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/key.md b/packages/dataparcels-docs/src/docs/api/parcelShape/key.md new file mode 100644 index 00000000..ebe60278 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/key.md @@ -0,0 +1,7 @@ +import Link from 'component/Link'; + +```flow +key: string +``` + +Returns the ParcelShape's `key`. Dataparcels automatically gives unique keys to all children of a parent parcel. See parcel keys for more info. diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/meta.md b/packages/dataparcels-docs/src/docs/api/parcelShape/meta.md new file mode 100644 index 00000000..97db82be --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/meta.md @@ -0,0 +1,6 @@ +import ParcelMeta from 'docs/notes/ParcelMeta.md'; + +```flow +meta: Object +``` +Returns an object containing the ParcelShapes's meta data. diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/move.md b/packages/dataparcels-docs/src/docs/api/parcelShape/move.md new file mode 100644 index 00000000..37e725b3 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/move.md @@ -0,0 +1,4 @@ +```flow +move(key: string|number): ParcelShape // only on ElementParcels, will move with sibling +move(keyA: string|number, keyB: string|number): ParcelShape // only on IndexedParcels, will move children +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/pop.md b/packages/dataparcels-docs/src/docs/api/parcelShape/pop.md new file mode 100644 index 00000000..6f8ca09b --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/pop.md @@ -0,0 +1,3 @@ +```flow +pop(): ParcelShape // only on IndexedParcels +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/push.md b/packages/dataparcels-docs/src/docs/api/parcelShape/push.md new file mode 100644 index 00000000..27ac4b12 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/push.md @@ -0,0 +1,3 @@ +```flow +push(...values: Array<*>): ParcelShape // only on IndexedParcels +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/set.md b/packages/dataparcels-docs/src/docs/api/parcelShape/set.md new file mode 100644 index 00000000..4dc1083f --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/set.md @@ -0,0 +1,30 @@ +import {Box, Link, Message} from 'dcme-style'; +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + +```flow +set(value: any): ParcelShape +set(key: string|number, value: any): ParcelShape // only on ParentParcels, will set a child +``` + +Calling `set()` with one argument will return a new ParcelShape where the original value is replaced with the `value` provided. + +```js +let parcelShape = new ParcelShape(123); +parcelShape.set(456); +// returns a new ParcelShape containing 456 +``` + +On ParentParcels this method can also be called with a `key`, which returns a new ParcelShape with the the child value at that `key` set to `value`. + +```js +let parcelShape = new ParcelShape({ + value: { + abc: 123, + def: 789 + } +}); +parcelShape.set('abc', 456); +// returns a new ParcelShape containing {abc: 456, def: 789} +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/setIn.md b/packages/dataparcels-docs/src/docs/api/parcelShape/setIn.md new file mode 100644 index 00000000..88197511 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/setIn.md @@ -0,0 +1,21 @@ +import IndexedKeys from 'docs/notes/IndexedKeys.md'; + +```flow +setIn(keyPath: Array, value: any): ParcelShape // only on ParentParcels +``` + +Calling `setIn()` will return a new ParcelShape where the value at the provided `keyPath` has been set to `value`. + +```js +let value = { + a: { + b: 123, + c: 789 + } +}; +let parcelShape = new ParcelShape(value); +parcelShape.set(['a','b'], 456); +// returns a new ParcelShape containing {a: {b: 456, c: 789}} +``` + + diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/setMeta.md b/packages/dataparcels-docs/src/docs/api/parcelShape/setMeta.md new file mode 100644 index 00000000..fb0eb796 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/setMeta.md @@ -0,0 +1,3 @@ +```flow +setMeta(partialMeta: Object): ParcelShape +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/shift.md b/packages/dataparcels-docs/src/docs/api/parcelShape/shift.md new file mode 100644 index 00000000..c15299b8 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/shift.md @@ -0,0 +1,3 @@ +```flow +shift(): ParcelShape // only on IndexedParcels +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/size.md b/packages/dataparcels-docs/src/docs/api/parcelShape/size.md new file mode 100644 index 00000000..3a6b29fd --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/size.md @@ -0,0 +1,5 @@ +```flow +size(): number // only on ParentParcels +``` + +Returns the number of children this ParcelShape has. diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/swap.md b/packages/dataparcels-docs/src/docs/api/parcelShape/swap.md new file mode 100644 index 00000000..f74d5d22 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/swap.md @@ -0,0 +1,4 @@ +```flow +swap(key: string|number): ParcelShape // only on ElementParcels, will swap with sibling +swap(keyA: string|number, keyB: string|number): ParcelShape // only on IndexedParcels, will swap children +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/swapNext.md b/packages/dataparcels-docs/src/docs/api/parcelShape/swapNext.md new file mode 100644 index 00000000..9b19a605 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/swapNext.md @@ -0,0 +1,4 @@ +```flow +swapNext(): ParcelShape // only on ElementParcels, will swap with next sibling +swapNext(key: string|number): ParcelShape // only on IndexedParcels, will swap child with next child +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/swapPrev.md b/packages/dataparcels-docs/src/docs/api/parcelShape/swapPrev.md new file mode 100644 index 00000000..dda6ae22 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/swapPrev.md @@ -0,0 +1,4 @@ +```flow +swapPrev(): ParcelShape // only on ElementParcels, will swap with previous sibling +swapPrev(key: string|number): ParcelShape // only on IndexedParcels, will swap child with previous child +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/toArray.md b/packages/dataparcels-docs/src/docs/api/parcelShape/toArray.md new file mode 100644 index 00000000..3efea360 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/toArray.md @@ -0,0 +1,21 @@ +```flow +toArray(): Array // only on ParentParcels +``` + +Like [children()](#children), expect the returned data structure is cast to an array. + +```js +let value = { + abc: 123, + def: 456 +}; + +let parcelShape = new ParcelShape(value); +parcelShape.toArray(); + +// returns [ +// ParcelShape, // contains a value of 123 +// ParcelShape // contains a value of 456 +// ] + +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/toConsole.md b/packages/dataparcels-docs/src/docs/api/parcelShape/toConsole.md new file mode 100644 index 00000000..8fd3e1d9 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/toConsole.md @@ -0,0 +1,5 @@ +```flow +toConsole(): void +``` + +Outputs the ParcelShape's data to the console. diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/toObject.md b/packages/dataparcels-docs/src/docs/api/parcelShape/toObject.md new file mode 100644 index 00000000..d58a7f7c --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/toObject.md @@ -0,0 +1,21 @@ +```flow +toObject(): {[key: string]: ParcelShape} // only on ParentParcels +``` + +Like [children()](#children), expect the returned data structure is cast to an object. + +```js +let value = { + abc: 123, + def: 456 +}; + +let parcelShape = new ParcelShape(value); +parcelShape.toObject() + +// returns { +// abc: ParcelShape, // contains a value of 123 +// def: ParcelShape // contains a value of 456 +// } + +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/unshift.md b/packages/dataparcels-docs/src/docs/api/parcelShape/unshift.md new file mode 100644 index 00000000..21b590c0 --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/unshift.md @@ -0,0 +1,3 @@ +```flow +unshift(...values: Array<*>): ParcelShape // only on IndexedParcels +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/update.md b/packages/dataparcels-docs/src/docs/api/parcelShape/update.md new file mode 100644 index 00000000..34f14e4d --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/update.md @@ -0,0 +1,4 @@ +```flow +update(updater: Function): ParcelShape +update(key: string|number, updater: Function): ParcelShape // only on ParentParcels, will set a child +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/updateIn.md b/packages/dataparcels-docs/src/docs/api/parcelShape/updateIn.md new file mode 100644 index 00000000..3816f1ad --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/updateIn.md @@ -0,0 +1,3 @@ +```flow +updateIn(keyPath: Array, updater: Function): ParcelShape // only on ParentParcels +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/updateShape.md b/packages/dataparcels-docs/src/docs/api/parcelShape/updateShape.md new file mode 100644 index 00000000..0b4bbedc --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/updateShape.md @@ -0,0 +1,4 @@ +```flow +updateShape(updater: Function): ParcelShape +updateShape(key: string|number, updater: Function): ParcelShape // only on ParentParcels, will set a child +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/updateShapeIn.md b/packages/dataparcels-docs/src/docs/api/parcelShape/updateShapeIn.md new file mode 100644 index 00000000..9f10252b --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/updateShapeIn.md @@ -0,0 +1,3 @@ +```flow +updateShapeIn(keyPath: Array, updater: Function): ParcelShape // only on ParentParcels +``` diff --git a/packages/dataparcels-docs/src/docs/api/parcelShape/value.md b/packages/dataparcels-docs/src/docs/api/parcelShape/value.md new file mode 100644 index 00000000..5a1052fb --- /dev/null +++ b/packages/dataparcels-docs/src/docs/api/parcelShape/value.md @@ -0,0 +1,5 @@ +```flow +value: any +``` + +Returns the ParcelShape's value. diff --git a/packages/dataparcels-docs/src/docs/notes/ChildKeys.md b/packages/dataparcels-docs/src/docs/notes/ChildKeys.md index cbb86168..c6e95535 100644 --- a/packages/dataparcels-docs/src/docs/notes/ChildKeys.md +++ b/packages/dataparcels-docs/src/docs/notes/ChildKeys.md @@ -2,5 +2,5 @@ import {Box, Message} from 'dcme-style'; import Link from 'component/Link'; - Parcels automatically gives unique keys to all children of a parent parcel. See parcel keys for more info. + Dataparcels automatically gives unique keys to all children of a parent parcel. See parcel keys for more info. diff --git a/packages/dataparcels-docs/src/docs/notes/IndexedKeys.md b/packages/dataparcels-docs/src/docs/notes/IndexedKeys.md index 7898eb3b..550d72fa 100644 --- a/packages/dataparcels-docs/src/docs/notes/IndexedKeys.md +++ b/packages/dataparcels-docs/src/docs/notes/IndexedKeys.md @@ -1,6 +1,12 @@ import {Box, Message} from 'dcme-style'; import Link from 'component/Link'; - - Parcels automatically gives unique keys to all elements of an indexed parcel. See parcel keys for more info. +When called on a Parcel with an indexed value, such as an array, this method can accept an `index` or a `key`. +- `index` (number) is used to get a value based off its position. It can also be negative, indicating an offset from the end of the sequence. +- `key` (string) is used to get a specific value by its unique key within the Parcel. + + + + Dataparcels automatically gives unique keys to all elements of an indexed parcel. See parcel keys for more info. + diff --git a/packages/dataparcels-docs/src/docs/notes/ValueUpdater.md b/packages/dataparcels-docs/src/docs/notes/ValueUpdater.md new file mode 100644 index 00000000..4d9bb66e --- /dev/null +++ b/packages/dataparcels-docs/src/docs/notes/ValueUpdater.md @@ -0,0 +1,45 @@ +import Link from 'component/Link'; +import {Box, Link as HtmlLink, Message} from 'dcme-style'; + +### Please be careful + + +This method is safe to use in most cases, but in some cases it should not be used: + +- If the updater gives you a primitive value or childless value, you can return anything. +- If the updater gives you a value that has children, you can always return a primitive value or childless value. +- If the updater gives you a value that has children, you can return a value with children **only if the shape hasn't changed**. + +Please ensure you do not change the shape of the value, as changing the data shape or moving children within the data shape can cause dataparcels to misplace its keying and meta information! +Dataparcels stores data against each part of a deep value's data structure, so it can only let you change the value if you promise not to alter the shape. + +```js +// example updaters +string => string + "!" // good +string => [string] // good +date => date.toString() // good +array => array.join(".") // good +array => array.map(number => number + 1) // good, shape is still the same + +array => array.slice(0,2) // bad, shape has changed if array is longer that 2! +array => array.reverse() // bad, shape has changed because items have moved around! +``` + +If you need to update the shape of the data, you can do so using `dataparcels/shape`. +The `shape` function will wrap your Parcel's data in a ParcelShape which allows for safe shape editing. See ParcelShape for more details. + +```js +import shape from 'dataparcels/shape'; + +let parcel = new Parcel({ + value: [1,2,3] +}); + +let modifiedParcel = parcel.modifyDown(shape(parcelShape => parcelShape + .push("foo") + .push("bar") + .setMeta({ + cool: true + }) +)); +``` diff --git a/packages/dataparcels-docs/src/examples/EditingArrays.jsx b/packages/dataparcels-docs/src/examples/EditingArrays.jsx index beaaa130..4c576725 100644 --- a/packages/dataparcels-docs/src/examples/EditingArrays.jsx +++ b/packages/dataparcels-docs/src/examples/EditingArrays.jsx @@ -1,5 +1,6 @@ import React from 'react'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; import ExampleHoc from 'component/ExampleHoc'; const FruitListParcelHoc = ParcelHoc({ @@ -22,6 +23,7 @@ const FruitListEditor = (props) => { + key {fruitParcel.key} } ; })} diff --git a/packages/dataparcels-docs/src/examples/EditingArraysDrag.jsx b/packages/dataparcels-docs/src/examples/EditingArraysDrag.jsx new file mode 100644 index 00000000..9bb43410 --- /dev/null +++ b/packages/dataparcels-docs/src/examples/EditingArraysDrag.jsx @@ -0,0 +1,34 @@ +import React from 'react'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; +import ParcelDrag from 'react-dataparcels-drag'; +import ExampleHoc from 'component/ExampleHoc'; + +const FruitListParcelHoc = ParcelHoc({ + name: "fruitListParcel", + valueFromProps: (/* props */) => [ + "Apple", + "Banana", + "Crumpets" + ] +}); + +const SortableFruitList = ParcelDrag({ + element: (fruitParcel) => + {(parcel) =>
+ + + +
} +
+}); + +const FruitListEditor = (props) => { + let {fruitListParcel} = props; + return
+ + +
; +}; + +export default FruitListParcelHoc(ExampleHoc(FruitListEditor)); diff --git a/packages/dataparcels-docs/src/examples/EditingArraysFlipMove.jsx b/packages/dataparcels-docs/src/examples/EditingArraysFlipMove.jsx index c378b791..27fe89fc 100644 --- a/packages/dataparcels-docs/src/examples/EditingArraysFlipMove.jsx +++ b/packages/dataparcels-docs/src/examples/EditingArraysFlipMove.jsx @@ -1,6 +1,7 @@ import React from 'react'; import FlipMove from 'react-flip-move'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; import ExampleHoc from 'component/ExampleHoc'; const FruitListParcelHoc = ParcelHoc({ diff --git a/packages/dataparcels-docs/src/examples/EditingModify.jsx b/packages/dataparcels-docs/src/examples/EditingModify.jsx new file mode 100644 index 00000000..695db161 --- /dev/null +++ b/packages/dataparcels-docs/src/examples/EditingModify.jsx @@ -0,0 +1,109 @@ +import React from 'react'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; +import CancelActionMarker from 'react-dataparcels/CancelActionMarker'; +import ExampleHoc from 'component/ExampleHoc'; + +const ExampleParcelHoc = ParcelHoc({ + name: "exampleParcel", + valueFromProps: (/* props */) => ({ + alphanumeric: "Abc123", + number: 123, + delimitedString: "abc.def", + missingValue: undefined + }) +}); + +const AlphanumericInput = (props) => { + let alphanumericParcel = props + .alphanumericParcel + .modifyUp(string => string.replace(/[^a-zA-Z0-9]/g, "")); + // ^ remove non alpha numeric characters on the way up + + return + {(parcel) => } + ; +}; + +const NumberInput = (props) => { + let numberParcel = props + .numberParcel + .modifyDown(number => `${number}`) + // ^ turn value into a string on the way down + .modifyUp(string => { + let number = Number(string); + return isNaN(number) ? CancelActionMarker : number; + }); + // ^ turn value back into a number on the way up + // but cancel the change if the string + // could not be turned into a number + + // without the keepState prop, typing "0.10" + // would immediately be replaced with "0.1" + // as the new value is turned into a number on the way up, + // and into a string on the way down + // which would make typing very frustrating + + return + {(parcel) => } + ; +}; + +const DelimitedStringInput = (props) => { + let delimitedStringParcel = props + .delimitedStringParcel + .modifyDown(string => string.split(".")) + // ^ turn value into an array on the way down + .modifyUp(array => array.join(".")); + // ^ turn value back into a string on the way up + + return
+ {delimitedStringParcel.toArray((segmentParcel) => { + return + {(parcel) =>
+ + + + +
} +
; + })} + +
; +}; + +const MissingValueInput = (props) => { + let missingValueParcel = props + .missingValueParcel + .modifyDown(value => value || []) + // ^ turn value into an array if its missing + // so we can always render against an array + + return
+ {missingValueParcel.toArray((segmentParcel) => { + return + {(parcel) => } + ; + })} + +
; +}; + +const ExampleEditor = (props) => { + let {exampleParcel} = props; + return
+

Alphanumeric input

+ + +

Number > string

+ + +

Delimited string > array of strings

+ + +

Coping with missing values

+ +
; +}; + +export default ExampleParcelHoc(ExampleHoc(ExampleEditor)); diff --git a/packages/dataparcels-docs/src/examples/EditingObjects.jsx b/packages/dataparcels-docs/src/examples/EditingObjects.jsx index 4ad2a24d..201e168c 100644 --- a/packages/dataparcels-docs/src/examples/EditingObjects.jsx +++ b/packages/dataparcels-docs/src/examples/EditingObjects.jsx @@ -1,5 +1,6 @@ import React from 'react'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; import ExampleHoc from 'component/ExampleHoc'; const PersonParcelHoc = ParcelHoc({ @@ -15,24 +16,19 @@ const PersonParcelHoc = ParcelHoc({ const PersonEditor = (props) => { let {personParcel} = props; - - let firstname = personParcel.get('firstname'); - let lastname = personParcel.get('lastname'); - let postcode = personParcel.getIn(['address', 'postcode']); - return
- + {(firstname) => } - + {(lastname) => } - + {(postcode) => }
; diff --git a/packages/dataparcels-docs/src/examples/EditingObjectsBeginner.jsx b/packages/dataparcels-docs/src/examples/EditingObjectsBeginner.jsx index 14d54079..c331c698 100644 --- a/packages/dataparcels-docs/src/examples/EditingObjectsBeginner.jsx +++ b/packages/dataparcels-docs/src/examples/EditingObjectsBeginner.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; import ExampleHoc from 'component/ExampleHoc'; const PersonParcelHoc = ParcelHoc({ diff --git a/packages/dataparcels-docs/src/examples/ManagingOwnParcelState.jsx b/packages/dataparcels-docs/src/examples/ManagingOwnParcelState.jsx index f53cadf5..225c4d23 100644 --- a/packages/dataparcels-docs/src/examples/ManagingOwnParcelState.jsx +++ b/packages/dataparcels-docs/src/examples/ManagingOwnParcelState.jsx @@ -1,5 +1,6 @@ import React from 'react'; -import Parcel, {ParcelBoundary} from 'react-dataparcels'; +import Parcel from 'react-dataparcels'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; export default class ManagingOwnParcelState extends React.Component { constructor(props) { diff --git a/packages/dataparcels-docs/src/examples/ModifyConditional.jsx b/packages/dataparcels-docs/src/examples/ModifyConditional.jsx new file mode 100644 index 00000000..40c6642a --- /dev/null +++ b/packages/dataparcels-docs/src/examples/ModifyConditional.jsx @@ -0,0 +1,35 @@ +import React from 'react'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; +import ExampleHoc from 'component/ExampleHoc'; + +const AgeParcelHoc = ParcelHoc({ + name: "ageParcel", + valueFromProps: (/* props */) => 22 +}); + +const NameEditor = (props) => { + let ageParcel = props + .ageParcel + .modifyDown(number => `${number}`) + // .modifyShapeUp(parcelShape => { + // let updated = parcelShape.update(string => Number(string)); + // return isNaN(updated.value) ? undefined : updated; + // }); + // .modifyChange((parcel, changeRequest) => { + // let string = changeRequest.nextData.value; + // let updated = Number(string); + // if(!isNaN(updated)) { + // parcel.set(updated); + // } + // }) + + return
+ + + {(ageParcel) => } + +
; +}; + +export default AgeParcelHoc(ExampleHoc(NameEditor)); diff --git a/packages/dataparcels-docs/src/examples/ParcelBoundaryDebounce.jsx b/packages/dataparcels-docs/src/examples/ParcelBoundaryDebounce.jsx index b503f735..b4bf4793 100644 --- a/packages/dataparcels-docs/src/examples/ParcelBoundaryDebounce.jsx +++ b/packages/dataparcels-docs/src/examples/ParcelBoundaryDebounce.jsx @@ -1,5 +1,6 @@ import React from 'react'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; import ExampleHoc from 'component/ExampleHoc'; const FoodParcelHoc = ParcelHoc({ diff --git a/packages/dataparcels-docs/src/examples/ParcelBoundaryExample.jsx b/packages/dataparcels-docs/src/examples/ParcelBoundaryExample.jsx deleted file mode 100644 index cb231bcd..00000000 --- a/packages/dataparcels-docs/src/examples/ParcelBoundaryExample.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; -import ExampleHoc from 'component/ExampleHoc'; - -const PersonParcelHoc = ParcelHoc({ - name: "personParcel", - valueFromProps: (/* props */) => ({ - name: { - first: "Robert", - last: "Clamps" - }, - age: "33" - }), - debugRender: true -}); - -const PersonEditor = (props) => { - let {personParcel} = props; - return
- - - {(name) =>
- - - {(first) => } - - - - - {(last) => } - -
- } -
- - - - {(age) => } - -
; -}; - -export default PersonParcelHoc(ExampleHoc(PersonEditor)); diff --git a/packages/dataparcels-docs/src/examples/ParcelBoundaryForceUpdate.jsx b/packages/dataparcels-docs/src/examples/ParcelBoundaryForceUpdate.jsx index 836d89d1..f16e242f 100644 --- a/packages/dataparcels-docs/src/examples/ParcelBoundaryForceUpdate.jsx +++ b/packages/dataparcels-docs/src/examples/ParcelBoundaryForceUpdate.jsx @@ -1,5 +1,6 @@ import React from 'react'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; import ExampleHoc from 'component/ExampleHoc'; const ColourParcelHoc = ParcelHoc({ diff --git a/packages/dataparcels-docs/src/examples/ParcelBoundaryHold.jsx b/packages/dataparcels-docs/src/examples/ParcelBoundaryHold.jsx index 836ecfaf..279cfea6 100644 --- a/packages/dataparcels-docs/src/examples/ParcelBoundaryHold.jsx +++ b/packages/dataparcels-docs/src/examples/ParcelBoundaryHold.jsx @@ -1,5 +1,6 @@ import React from 'react'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; import ExampleHoc from 'component/ExampleHoc'; const NameParcelHoc = ParcelHoc({ @@ -7,7 +8,7 @@ const NameParcelHoc = ParcelHoc({ valueFromProps: (/* props */) => "Gregor" }); -const FoodEditor = (props) => { +const NameEditor = (props) => { let {nameParcel} = props; return
@@ -21,4 +22,4 @@ const FoodEditor = (props) => {
; }; -export default NameParcelHoc(ExampleHoc(FoodEditor)); +export default NameParcelHoc(ExampleHoc(NameEditor)); diff --git a/packages/dataparcels-docs/src/examples/ParcelBoundaryPure.jsx b/packages/dataparcels-docs/src/examples/ParcelBoundaryPure.jsx new file mode 100644 index 00000000..7a6f71e6 --- /dev/null +++ b/packages/dataparcels-docs/src/examples/ParcelBoundaryPure.jsx @@ -0,0 +1,68 @@ +import React from 'react'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; +import ExampleHoc from 'component/ExampleHoc'; + +const PersonParcelHoc = ParcelHoc({ + name: "personParcel", + valueFromProps: (/* props */) => ({ + name: { + first: "Robert", + last: "Clamps" + }, + age: "33", + height: "160" + }) +}); + +const DebugRender = ({children}) => { + // each render, have a new, random background colour + let rand = () => Math.floor((Math.random() * 0.75 + 0.25) * 256); + let style = { + backgroundColor: `rgb(${rand()},${rand()},${rand()})`, + padding: "1rem", + marginBottom: "1rem" + }; + return
{children}
; +}; + +const PersonEditor = (props) => { + let {personParcel} = props; + return
+ + + {(name) => + + + {(first) => + + } + + + + + {(last) => + + } + + + } + + + + + {(age) => + + } + + + + + {(height) => + + } + +
; +}; + +export default PersonParcelHoc(ExampleHoc(PersonEditor)); diff --git a/packages/dataparcels-docs/src/examples/ParcelHocExample.jsx b/packages/dataparcels-docs/src/examples/ParcelHocExample.jsx index 57def380..6d710503 100644 --- a/packages/dataparcels-docs/src/examples/ParcelHocExample.jsx +++ b/packages/dataparcels-docs/src/examples/ParcelHocExample.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; import ExampleHoc from 'component/ExampleHoc'; const WordParcelHoc = ParcelHoc({ diff --git a/packages/dataparcels-docs/src/examples/ParcelHocExampleDelayUntil.jsx b/packages/dataparcels-docs/src/examples/ParcelHocExampleDelayUntil.jsx index cd5db144..b73a3ebd 100644 --- a/packages/dataparcels-docs/src/examples/ParcelHocExampleDelayUntil.jsx +++ b/packages/dataparcels-docs/src/examples/ParcelHocExampleDelayUntil.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; import ExampleHoc from 'component/ExampleHoc'; const DelayParcelHoc = ParcelHoc({ diff --git a/packages/dataparcels-docs/src/examples/ParcelHocExampleInitialValueFromProps.jsx b/packages/dataparcels-docs/src/examples/ParcelHocExampleInitialValueFromProps.jsx index 27bf85b7..2b92672b 100644 --- a/packages/dataparcels-docs/src/examples/ParcelHocExampleInitialValueFromProps.jsx +++ b/packages/dataparcels-docs/src/examples/ParcelHocExampleInitialValueFromProps.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; import ExampleHoc from 'component/ExampleHoc'; const WordParcelHoc = ParcelHoc({ diff --git a/packages/dataparcels-docs/src/examples/ParcelHocExampleOnChange.jsx b/packages/dataparcels-docs/src/examples/ParcelHocExampleOnChange.jsx index b0e7a5fa..2f909c42 100644 --- a/packages/dataparcels-docs/src/examples/ParcelHocExampleOnChange.jsx +++ b/packages/dataparcels-docs/src/examples/ParcelHocExampleOnChange.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; import ExampleHoc from 'component/ExampleHoc'; const WordParcelHoc = ParcelHoc({ diff --git a/packages/dataparcels-docs/src/examples/ParcelHocUpdateFromProps.jsx b/packages/dataparcels-docs/src/examples/ParcelHocUpdateFromProps.jsx index 1fe55058..3c066b3e 100644 --- a/packages/dataparcels-docs/src/examples/ParcelHocUpdateFromProps.jsx +++ b/packages/dataparcels-docs/src/examples/ParcelHocUpdateFromProps.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; import ExampleHoc from 'component/ExampleHoc'; import {Box} from 'dcme-style'; diff --git a/packages/dataparcels-docs/src/examples/ParcelHocUpdateFromQueryString.jsx b/packages/dataparcels-docs/src/examples/ParcelHocUpdateFromQueryString.jsx index 9d3b0b74..95541a52 100644 --- a/packages/dataparcels-docs/src/examples/ParcelHocUpdateFromQueryString.jsx +++ b/packages/dataparcels-docs/src/examples/ParcelHocUpdateFromQueryString.jsx @@ -1,6 +1,6 @@ import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; -import {ParcelBoundary} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; import ExampleHoc from 'component/ExampleHoc'; import IsRenderingStaticHtml from 'utils/IsRenderingStaticHtml'; import ReactRouterQueryStringHoc from 'react-cool-storage/lib/ReactRouterQueryStringHoc'; diff --git a/packages/dataparcels-docs/src/examples/ParcelMetaChangedValues.jsx b/packages/dataparcels-docs/src/examples/ParcelMetaChangedValues.jsx new file mode 100644 index 00000000..d1e5142e --- /dev/null +++ b/packages/dataparcels-docs/src/examples/ParcelMetaChangedValues.jsx @@ -0,0 +1,48 @@ +import React from 'react'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; +import ExampleHoc from 'component/ExampleHoc'; + +const PersonParcelHoc = ParcelHoc({ + name: "personParcel", + valueFromProps: (/* props */) => ({ + firstname: "Robert", + lastname: "Clamps" + }) +}); + +const withOriginalMeta = (parcel) => parcel.initialMeta({ + original: parcel.value +}); + +const PersonEditor = (props) => { + let {personParcel} = props; + + let firstname = personParcel + .get('firstname') + .pipe(withOriginalMeta); + + let lastname = personParcel + .get('lastname') + .pipe(withOriginalMeta); + + return
+ + + {(firstname) =>
+ +
Changed? {firstname.meta.original === firstname.value ? 'No' : 'Yes'}
+
} +
+ + + + {(lastname) =>
+ +
Changed? {lastname.meta.original === lastname.value ? 'No' : 'Yes'}
+
} +
+
; +}; + +export default PersonParcelHoc(ExampleHoc(PersonEditor)); diff --git a/packages/dataparcels-docs/src/examples/ParcelMetaConfirmingDeletions.jsx b/packages/dataparcels-docs/src/examples/ParcelMetaConfirmingDeletions.jsx new file mode 100644 index 00000000..f1852c1c --- /dev/null +++ b/packages/dataparcels-docs/src/examples/ParcelMetaConfirmingDeletions.jsx @@ -0,0 +1,35 @@ +import React from 'react'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; +import ExampleHoc from 'component/ExampleHoc'; + +const FruitListParcelHoc = ParcelHoc({ + name: "fruitListParcel", + valueFromProps: (/* props */) => [ + "Apple", + "Banana", + "Crumpets" + ] +}); + +const FruitListEditor = (props) => { + let {fruitListParcel} = props; + return
+ {fruitListParcel.toArray((fruitParcel) => { + return + {(parcel) =>
+ + {parcel.meta.confirming + ? Are you sure? + + + + : } +
} +
; + })} + +
; +}; + +export default FruitListParcelHoc(ExampleHoc(FruitListEditor)); diff --git a/packages/dataparcels-docs/src/layouts/index.scss b/packages/dataparcels-docs/src/layouts/index.scss index 9a8b98c7..05c03937 100644 --- a/packages/dataparcels-docs/src/layouts/index.scss +++ b/packages/dataparcels-docs/src/layouts/index.scss @@ -6,6 +6,23 @@ @include DcmeBase; @include DcmeBox { + &-draggable { + cursor: move; + padding-left: 1rem; + position: relative; + + &:after { + color: grey(70); + content: '.. .. .. ..'; + display: block; + font-size: 1.2rem; + left: 0; + line-height: 5px; + position: absolute; + top: 0; + width: 1rem; + } + } &-example { margin-left: -2rem; margin-right: -2rem; @@ -18,7 +35,10 @@ @include DcmeBulletList; @include DcmeButton; @include DcmeCenteredLanding; -@include DcmeDivider; +@include DcmeDivider { + border-top: 1px solid #CCC; + margin: 5rem 0 4rem; +} @include DcmeGrid; @include DcmeImage { &-logo { @@ -63,5 +83,31 @@ -12px 12px 0px lighten(color('primary'), 30) } } -@include DcmeTypography; +@include DcmeTypography { + input { + background: #FFF; + } +} @include DcmeWrapper; + +// .DragHandle { +// content: '....'; +// width: 10px; +// height: 20px; +// display: inline-block; +// overflow: hidden; +// line-height: 5px; +// padding: 3px 4px; +// cursor: move; +// vertical-align: middle; +// margin-top: -.7em; +// margin-right: .3em; +// font-size: 12px; +// font-family: sans-serif; +// letter-spacing: 2px; +// color: #cccccc; +// text-shadow: 1px 0 1px black; +// } +// .DragHandle::after { +// content: '.. .. .. ..'; +// } diff --git a/packages/dataparcels-docs/src/pages/api/Action.jsx b/packages/dataparcels-docs/src/pages/api/Action.jsx index edfefe3a..77ec4b7c 100644 --- a/packages/dataparcels-docs/src/pages/api/Action.jsx +++ b/packages/dataparcels-docs/src/pages/api/Action.jsx @@ -3,14 +3,12 @@ import type {Node} from 'react'; import React from 'react'; import ApiPage from 'component/ApiPage'; import Markdown_Action from 'docs/api/action/Action.md'; -import Markdown_shouldBeSynchronous from 'docs/api/action/shouldBeSynchronous.md'; import Markdown_isValueAction from 'docs/api/action/isValueAction.md'; import Markdown_isMetaAction from 'docs/api/action/isMetaAction.md'; import Markdown_toJS from 'docs/api/action/toJS.md'; const md = { _desc: Markdown_Action, - shouldBeSynchronous: Markdown_shouldBeSynchronous, isValueAction: Markdown_isValueAction, isMetaAction: Markdown_isMetaAction, toJS: Markdown_toJS @@ -18,7 +16,6 @@ const md = { const api = ` # Methods -shouldBeSynchronous() isValueAction() isMetaAction() toJS() diff --git a/packages/dataparcels-docs/src/pages/api/ChangeRequest.jsx b/packages/dataparcels-docs/src/pages/api/ChangeRequest.jsx index 8ac92e56..ce622c7a 100644 --- a/packages/dataparcels-docs/src/pages/api/ChangeRequest.jsx +++ b/packages/dataparcels-docs/src/pages/api/ChangeRequest.jsx @@ -23,7 +23,6 @@ updateActions() merge() getDataIn() hasValueChanged() -shouldBeSynchronous() toJS() toConsole() `; diff --git a/packages/dataparcels-docs/src/pages/api/Parcel.jsx b/packages/dataparcels-docs/src/pages/api/Parcel.jsx index 5deff793..b4a6f084 100644 --- a/packages/dataparcels-docs/src/pages/api/Parcel.jsx +++ b/packages/dataparcels-docs/src/pages/api/Parcel.jsx @@ -14,6 +14,7 @@ import Markdown_spread from 'docs/api/parcel/spread.md'; import Markdown_spreadDOM from 'docs/api/parcel/spreadDOM.md'; import Markdown_get from 'docs/api/parcel/get.md'; import Markdown_getIn from 'docs/api/parcel/getIn.md'; +import Markdown_children from 'docs/api/parcel/children.md'; import Markdown_toObject from 'docs/api/parcel/toObject.md'; import Markdown_toArray from 'docs/api/parcel/toArray.md'; import Markdown_has from 'docs/api/parcel/has.md'; @@ -30,6 +31,7 @@ import Markdown_delete from 'docs/api/parcel/delete.md'; import Markdown_deleteIn from 'docs/api/parcel/deleteIn.md'; import Markdown_insertAfter from 'docs/api/parcel/insertAfter.md'; import Markdown_insertBefore from 'docs/api/parcel/insertBefore.md'; +import Markdown_move from 'docs/api/parcel/move.md'; import Markdown_pop from 'docs/api/parcel/pop.md'; import Markdown_push from 'docs/api/parcel/push.md'; import Markdown_shift from 'docs/api/parcel/shift.md'; @@ -38,27 +40,19 @@ import Markdown_swapNext from 'docs/api/parcel/swapNext.md'; import Markdown_swapPrev from 'docs/api/parcel/swapPrev.md'; import Markdown_unshift from 'docs/api/parcel/unshift.md'; import Markdown_setMeta from 'docs/api/parcel/setMeta.md'; -import Markdown_updateMeta from 'docs/api/parcel/updateMeta.md'; -import Markdown_setChangeRequestMeta from 'docs/api/parcel/setChangeRequestMeta.md'; import Markdown_dispatch from 'docs/api/parcel/dispatch.md'; -import Markdown_batch from 'docs/api/parcel/batch.md'; -import Markdown_batchAndReturn from 'docs/api/parcel/batchAndReturn.md'; -import Markdown_ping from 'docs/api/parcel/ping.md'; -import Markdown_modifyValue from 'docs/api/parcel/modifyValue.md'; -import Markdown_modifyChangeBatch from 'docs/api/parcel/modifyChangeBatch.md'; -import Markdown_modifyChangeValue from 'docs/api/parcel/modifyChangeValue.md'; +import Markdown_modifyDown from 'docs/api/parcel/modifyDown.md'; +import Markdown_modifyUp from 'docs/api/parcel/modifyUp.md'; import Markdown_initialMeta from 'docs/api/parcel/initialMeta.md'; import Markdown_isChild from 'docs/api/parcel/isChild.md'; import Markdown_isElement from 'docs/api/parcel/isElement.md'; import Markdown_isIndexed from 'docs/api/parcel/isIndexed.md'; import Markdown_isParent from 'docs/api/parcel/isParent.md'; import Markdown_isTopLevel from 'docs/api/parcel/isTopLevel.md'; -import Markdown_hasDispatched from 'docs/api/parcel/hasDispatched.md'; import Markdown_log from 'docs/api/parcel/log.md'; import Markdown_spy from 'docs/api/parcel/spy.md'; import Markdown_spyChange from 'docs/api/parcel/spyChange.md'; import Markdown_pipe from 'docs/api/parcel/pipe.md'; -import Markdown_matchPipe from 'docs/api/parcel/matchPipe.md'; import Markdown_toConsole from 'docs/api/parcel/toConsole.md'; const md = { @@ -74,6 +68,7 @@ const md = { spreadDOM: Markdown_spreadDOM, get: Markdown_get, getIn: Markdown_getIn, + children: Markdown_children, toObject: Markdown_toObject, toArray: Markdown_toArray, has: Markdown_has, @@ -90,6 +85,7 @@ const md = { deleteIn: Markdown_deleteIn, insertAfter: Markdown_insertAfter, insertBefore: Markdown_insertBefore, + move: Markdown_move, pop: Markdown_pop, push: Markdown_push, shift: Markdown_shift, @@ -98,27 +94,19 @@ const md = { swapPrev: Markdown_swapPrev, unshift: Markdown_unshift, setMeta: Markdown_setMeta, - updateMeta: Markdown_updateMeta, - setChangeRequestMeta: Markdown_setChangeRequestMeta, dispatch: Markdown_dispatch, - batch: Markdown_batch, - batchAndReturn: Markdown_batchAndReturn, - ping: Markdown_ping, - modifyValue: Markdown_modifyValue, - modifyChangeBatch: Markdown_modifyChangeBatch, - modifyChangeValue: Markdown_modifyChangeValue, + modifyDown: Markdown_modifyDown, + modifyUp: Markdown_modifyUp, initialMeta: Markdown_initialMeta, isChild: Markdown_isChild, isElement: Markdown_isElement, isIndexed: Markdown_isIndexed, isParent: Markdown_isParent, isTopLevel: Markdown_isTopLevel, - hasDispatched: Markdown_hasDispatched, log: Markdown_log, spy: Markdown_spy, spyChange: Markdown_spyChange, pipe: Markdown_pipe, - matchPipe: Markdown_matchPipe, toConsole: Markdown_toConsole } @@ -131,37 +119,37 @@ key id path -# Spread methods -spread() -spreadDOM() - # Branch methods get() getIn() +children() toObject() toArray() -# Parent methods -has() -size() +# Input binding methods +spread() +spreadDOM() +onChange() +onChangeDOM() # Child methods isFirst() isLast() # Change methods -onChange() -onChangeDOM() set() setIn() -update() -updateIn() delete() deleteIn() +update() +updateIn() +setMeta() +dispatch() # Indexed & element change methods insertAfter() insertBefore() +move() push() pop() shift() @@ -170,20 +158,9 @@ swapNext() swapPrev() unshift() -# Advanced change methods -setMeta() -updateMeta() -setChangeRequestMeta() -dispatch() -batch() -batchAndReturn() -ping() - -# Modify methods -modifyValue() -modifyChangeBatch() -modifyChangeValue() -initialMeta() +# Parent methods +has() +size() # Type methods isChild() @@ -192,8 +169,10 @@ isIndexed() isParent() isTopLevel() -# Status methods -hasDispatched() +# Modify methods +modifyDown() +modifyUp() +initialMeta() # Side-effect methods spy() @@ -201,12 +180,10 @@ spyChange() # Composition methods pipe() -matchPipe() # Debug methods log() toConsole() - `; export default () => ; diff --git a/packages/dataparcels-docs/src/pages/data-editing.js b/packages/dataparcels-docs/src/pages/data-editing.js new file mode 100644 index 00000000..887befd8 --- /dev/null +++ b/packages/dataparcels-docs/src/pages/data-editing.js @@ -0,0 +1,11 @@ +// @flow +import type {Node} from 'react'; +import React from 'react'; +import {Wrapper, Text, Typography} from 'dcme-style'; +import Markdown from 'pages/data-editing.md'; +import PageLayout from 'component/PageLayout'; + +export default () => } +/>; diff --git a/packages/dataparcels-docs/src/pages/data-editing.md b/packages/dataparcels-docs/src/pages/data-editing.md new file mode 100644 index 00000000..991a829b --- /dev/null +++ b/packages/dataparcels-docs/src/pages/data-editing.md @@ -0,0 +1,183 @@ +import Link from 'gatsby-link'; +import EditingObjects from 'examples/EditingObjects'; +import EditingArrays from 'examples/EditingArrays'; +import EditingModify from 'examples/EditingModify'; +import ManagingOwnParcelState from 'examples/ManagingOwnParcelState'; + +# Data Editing + +Data editing is the ability to manipulate data based on user input, in a way that's expressive to code. + +The core of dataparcels' flexible data editing capabilities come from its Parcel class and methods. It provides methods to let you **traverse data structures** and **bind data to inputs**, so each input is connected to a specific piece of data in the Parcel. Any changes that occur in each input are propagated back to the top level Parcel, which takes care of **merging partial changes** back into the original data structure. + +Say we want to allow the user to edit the fields in the following data structure: + +```js +{ + firstname: "Robert", + lastname: "Clamps", + address: { + postcode: "1234" + } +} +``` + +This example demonstrates a pretty typical React setup to do that. + +EditingObjects /> + +```js +import React from 'react'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; + +const PersonParcelHoc = ParcelHoc({ + name: "personParcel", + valueFromProps: (/* props */) => ({ + firstname: "Robert", + lastname: "Clamps", + address: { + postcode: "1234" + } + }) +}); + +const PersonEditor = (props) => { + let {personParcel} = props; + return
+ + + {(firstname) => } + + + + + {(lastname) => } + + + + + {(postcode) => } + +
; +}; + +export default PersonParcelHoc(PersonEditor); + +``` + +### What's going on + +* `react-dataparcels` is imported. +* It stores the data in a ParcelHoc higher order component, which creates and stores a Parcel in state, and passes it down as props. +* The `.get()` and `getIn()` methods are used on the Parcel to create smaller parcels containing just `firstname`, `lastname` and `postcode` respectively. +* It uses the ParcelBoundary React component to avoid needless re-rendering. This isn't *required*, but it is very recommended. +* Finally `.spreadDOM()` is used to provide the `value` and `onChange` props to the `input` elements. + +For the full list of methods you can use to traverse and change Parcels, see Branch Methods, Input Binding Methods and Change Methods in the Parcel API reference. + +## Indexed data types + +Dataparcels has a powerful set of methods for manipulating indexed data types, such as arrays. This example demonstrates an editor that allows the user to edit, append to and sort the elements in an array of strings. + +Notice how items in the array are given **automatic unique keys**, displayed under each input as `#a`, `#b` ..., which can be used by React to identify each element regardless of how the elements move around. + +EditingArrays /> + +```js +import React from 'react'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; +import ExampleHoc from 'component/ExampleHoc'; + +const FruitListParcelHoc = ParcelHoc({ + name: "fruitListParcel", + valueFromProps: (/* props */) => [ + "Apple", + "Banana", + "Crumpets" + ] +}); + +const FruitListEditor = (props) => { + let {fruitListParcel} = props; + return
+ {fruitListParcel.toArray((fruitParcel) => { + return + {(parcel) =>
+ + + + + + key {fruitParcel.key} +
} +
; + })} + +
; +}; + +export default FruitListParcelHoc(FruitListEditor); +``` + +### What's going on + +* `fruitListParcel` contains an array. +* `Parcel.toArray()` is used to iterate over the Parcel's elements, and it is passed a `mapper` function to return React elements. +* Each element parcel's `key` property is used to uniquely key each React element. +* `ParcelBoundary` is used to ensure great rendering performance. + +For the full list of methods you can use on indexed data types, see Indexed Change Methods and Element Change Methods in the Parcel API reference. + +## Modifying data to fit the UI + +Sometimes you may hit a situation where a Parcel contains data you want to be able to make an editor for, but the data isn't stored in a format that allows you to do that easily. Parcel's modifyDown() and modifyUp() methods let you change data types and shapes between the top level Parcel and the input bindings. + + + +## Managing your own Parcel state + +If you don't want to use the ParcelHoc higher order component and would prefer to manage your Parcel's state yourself, this example deomstrates how. + +This example also serves as an indication on how you might use `dataparcels` with something other than React. + +ManagingOwnParcelState /> + +```js +import React from 'react'; +import Parcel from 'react-dataparcels'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; + +export default class ManagingOwnParcelState extends React.Component { + constructor(props) { + super(props); + + let personParcel = new Parcel({ + value: { + firstname: "Robert", + lastname: "Clamps" + }, + handleChange: (personParcel) => this.setState({personParcel}) + }); + + this.state = {personParcel}; + } + + render() { + let {personParcel} = this.state; + return
+ + + {(firstname) => } + + + + + {(lastname) => } + +
; + } +} + +``` diff --git a/packages/dataparcels-docs/src/pages/examples/editing-arrays.js b/packages/dataparcels-docs/src/pages/examples/editing-arrays.js deleted file mode 100644 index 39920acc..00000000 --- a/packages/dataparcels-docs/src/pages/examples/editing-arrays.js +++ /dev/null @@ -1,9 +0,0 @@ -// @flow -import React from 'react'; -import Markdown from 'pages/examples/editing-arrays.md'; -import Example from 'component/Example'; - -export default () => ; diff --git a/packages/dataparcels-docs/src/pages/examples/editing-arrays.md b/packages/dataparcels-docs/src/pages/examples/editing-arrays.md deleted file mode 100644 index c56d16aa..00000000 --- a/packages/dataparcels-docs/src/pages/examples/editing-arrays.md +++ /dev/null @@ -1,94 +0,0 @@ -import Link from 'gatsby-link'; -import EditingArrays from 'examples/EditingArrays'; -import EditingArraysFlipMove from 'examples/EditingArraysFlipMove'; - -Dataparcels has a powerful set of methods for manipulating indexed data types, such as arrays. This example demonstrates an editor that allows the user to edit, append to and sort the elements in an array of strings. - - - -```js -import React from 'react'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; -import ExampleHoc from 'component/ExampleHoc'; - -const FruitListParcelHoc = ParcelHoc({ - name: "fruitListParcel", - valueFromProps: (/* props */) => [ - "Apple", - "Banana", - "Crumpets" - ] -}); - -const FruitListEditor = (props) => { - let {fruitListParcel} = props; - return
- {fruitListParcel.toArray((fruitParcel) => { - return - {(parcel) =>
- - - - - -
} -
; - })} - -
; -}; - -export default FruitListParcelHoc(FruitListEditor); -``` - -### What's going on - -* `fruitListParcel` contains an array. -* `Parcel.toArray()` is used to iterate over the Parcel's elements, and it is passed a `mapper` function to return React elements. -* Each element parcel's `key` property is used to uniquely key each React element. -* `ParcelBoundary` is used to ensure great rendering performance. - -For the full list of methods you can use on indexed data types, see Indexed Change Methods and Element Change Methods in the Parcel API reference. - -## With react-flip-move - -Dataparcels automatic keying plays nicely with [react-flip-move](https://github.com/joshwcomeau/react-flip-move). - - - -```js -import React from 'react'; -import FlipMove from 'react-flip-move'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; -import ExampleHoc from 'component/ExampleHoc'; - -const FruitListEditor = (props) => { - let {fruitListParcel} = props; - return - {fruitListParcel.toArray((fruitParcel) => { - return - {(parcel) =>
- - - - - -
} -
; - })} - -
; -}; - -const FruitListParcelHoc = ParcelHoc({ - valueFromProps: (/* props */) => [ - "Apple", - "Banana", - "Crumpets" - ], - name: "fruitListParcel" -}); - -export default FruitListParcelHoc(ExampleHoc(FruitListEditor)); - -``` diff --git a/packages/dataparcels-docs/src/pages/examples/editing-objects.md b/packages/dataparcels-docs/src/pages/examples/editing-objects.md deleted file mode 100644 index 23c533b6..00000000 --- a/packages/dataparcels-docs/src/pages/examples/editing-objects.md +++ /dev/null @@ -1,70 +0,0 @@ -import EditingObjects from 'examples/EditingObjects'; - -Say we want to allow the user to edit the fields in the following data structure: - -```js -{ - firstname: "Robert", - lastname: "Clamps", - address: { - postcode: "1234" - } -} -``` - -This example demonstrates a pretty typical React setup to do that. - - - -```js -import React from 'react'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; - -const PersonParcelHoc = ParcelHoc({ - name: "personParcel", - valueFromProps: (/* props */) => ({ - firstname: "Robert", - lastname: "Clamps", - address: { - postcode: "1234" - } - }) -}); - -const PersonEditor = (props) => { - let {personParcel} = props; - - let firstname = personParcel.get('firstname'); - let lastname = personParcel.get('lastname'); - let postcode = personParcel.getIn(['address', 'postcode']); - - return
- - - {(firstname) => } - - - - - {(lastname) => } - - - - - {(postcode) => } - -
; -}; - -export default PersonParcelHoc(PersonEditor); - -``` - -### What's going on - -* `react-dataparcels` is imported. -* It stores the data in a `ParcelHoc` higher order component, which creates and stores a parcel in state, and passes it down as props. -* The `.get()` method is used on the parcel to create smaller parcels containing just `firstname` and `lastname`. -* It uses the `ParcelBoundary` React component to avoid needless re-rendering. This isn't *required*, but it is very recommended. -* Finally `.spreadDOM()` is used to provide the `value` and `onChange` props to the `input` elements. - diff --git a/packages/dataparcels-docs/src/pages/examples/managing-your-own-parcel-state.js b/packages/dataparcels-docs/src/pages/examples/managing-your-own-parcel-state.js deleted file mode 100644 index 817120f2..00000000 --- a/packages/dataparcels-docs/src/pages/examples/managing-your-own-parcel-state.js +++ /dev/null @@ -1,9 +0,0 @@ -// @flow -import React from 'react'; -import Markdown from 'pages/examples/managing-your-own-parcel-state.md'; -import Example from 'component/Example'; - -export default () => ; diff --git a/packages/dataparcels-docs/src/pages/examples/managing-your-own-parcel-state.md b/packages/dataparcels-docs/src/pages/examples/managing-your-own-parcel-state.md deleted file mode 100644 index a2199a8b..00000000 --- a/packages/dataparcels-docs/src/pages/examples/managing-your-own-parcel-state.md +++ /dev/null @@ -1,43 +0,0 @@ -import Link from 'gatsby-link'; -import ManagingOwnParcelState from 'examples/ManagingOwnParcelState'; - -If you don't want to use the ParcelHoc higher order component and would prefer to manage your Parcel's state yourself, this example deomstrates how. - - - -```js -import React from 'react'; -import Parcel, {ParcelBoundary} from 'react-dataparcels'; - -export default class ManagingOwnParcelState extends React.Component { - constructor(props) { - super(props); - - let personParcel = new Parcel({ - value: { - firstname: "Robert", - lastname: "Clamps" - }, - handleChange: (personParcel) => this.setState({personParcel}) - }); - - this.state = {personParcel}; - } - - render() { - let {personParcel} = this.state; - return
- - - {(firstname) => } - - - - - {(lastname) => } - -
; - } -} - -``` diff --git a/packages/dataparcels-docs/src/pages/examples/parcelboundary-debounce.md b/packages/dataparcels-docs/src/pages/examples/parcelboundary-debounce.md index 51a81e0c..2e44e361 100644 --- a/packages/dataparcels-docs/src/pages/examples/parcelboundary-debounce.md +++ b/packages/dataparcels-docs/src/pages/examples/parcelboundary-debounce.md @@ -9,7 +9,8 @@ This example demonstrates ParcelBoundary's `debounce` feature. The first field i ```js import React from 'react'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; const FoodParcelHoc = ParcelHoc({ name: "foodParcel", diff --git a/packages/dataparcels-docs/src/pages/examples/parcelboundary-example.js b/packages/dataparcels-docs/src/pages/examples/parcelboundary-example.js deleted file mode 100644 index c76b6c56..00000000 --- a/packages/dataparcels-docs/src/pages/examples/parcelboundary-example.js +++ /dev/null @@ -1,9 +0,0 @@ -// @flow -import React from 'react'; -import Markdown from 'pages/examples/parcelboundary-example.md'; -import Example from 'component/Example'; - -export default () => ; diff --git a/packages/dataparcels-docs/src/pages/examples/parcelboundary-example.md b/packages/dataparcels-docs/src/pages/examples/parcelboundary-example.md deleted file mode 100644 index 57617d23..00000000 --- a/packages/dataparcels-docs/src/pages/examples/parcelboundary-example.md +++ /dev/null @@ -1,53 +0,0 @@ -import Link from 'gatsby-link'; -import ParcelBoundaryExample from 'examples/ParcelBoundaryExample'; - -This example demonstrates nested ParcelBoundaries and their pure rendering feature. ParcelBoundaries appear as coloured boxes. As you type in an input, the colours will change to indicate which ParcelBoundaries have re-rendered. - -API reference for ParcelBoundary - - - -```js -import React from 'react'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; - -const PersonParcelHoc = ParcelHoc({ - name: "personParcel", - valueFromProps: (/* props */) => ({ - name: { - first: "Robert", - last: "Clamps" - }, - age: "33" - }), - debugRender: true -}); - -const PersonEditor = (props) => { - let {personParcel} = props; - return
- - - {(name) =>
- - - {(first) => } - - - - - {(last) => } - -
- } -
- - - - {(age) => } - -
; -}; - -export default PersonParcelHoc(PersonEditor); -``` diff --git a/packages/dataparcels-docs/src/pages/examples/parcelboundary-forceUpdate.md b/packages/dataparcels-docs/src/pages/examples/parcelboundary-forceUpdate.md index d3d25cef..7cfc3eed 100644 --- a/packages/dataparcels-docs/src/pages/examples/parcelboundary-forceUpdate.md +++ b/packages/dataparcels-docs/src/pages/examples/parcelboundary-forceUpdate.md @@ -11,7 +11,8 @@ The `forceUpdate` option is used to force the ParcelBoundary to update when `opt ```js import React from 'react'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; const ColourParcelHoc = ParcelHoc({ name: "colourParcel", diff --git a/packages/dataparcels-docs/src/pages/examples/parcelboundary-hold.md b/packages/dataparcels-docs/src/pages/examples/parcelboundary-hold.md index 577a736a..a76ddfcc 100644 --- a/packages/dataparcels-docs/src/pages/examples/parcelboundary-hold.md +++ b/packages/dataparcels-docs/src/pages/examples/parcelboundary-hold.md @@ -9,14 +9,15 @@ This example demonstrates ParcelBoundary's `hold` feature. ```js import React from 'react'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; const NameParcelHoc = ParcelHoc({ name: "nameParcel", valueFromProps: (/* props */) => "Gregor" }); -const FoodEditor = (props) => { +const NameEditor = (props) => { let {nameParcel} = props; return
@@ -30,5 +31,5 @@ const FoodEditor = (props) => {
; }; -export default NameParcelHoc(FoodEditor); +export default NameParcelHoc(NameEditor); ``` diff --git a/packages/dataparcels-docs/src/pages/examples/editing-objects.js b/packages/dataparcels-docs/src/pages/examples/parcelboundary-pure.js similarity index 53% rename from packages/dataparcels-docs/src/pages/examples/editing-objects.js rename to packages/dataparcels-docs/src/pages/examples/parcelboundary-pure.js index 7bce9993..58bf7ed2 100644 --- a/packages/dataparcels-docs/src/pages/examples/editing-objects.js +++ b/packages/dataparcels-docs/src/pages/examples/parcelboundary-pure.js @@ -1,9 +1,9 @@ // @flow import React from 'react'; -import Markdown from 'pages/examples/editing-objects.md'; +import Markdown from 'pages/examples/parcelboundary-pure.md'; import Example from 'component/Example'; export default () => ; diff --git a/packages/dataparcels-docs/src/pages/examples/parcelboundary-pure.md b/packages/dataparcels-docs/src/pages/examples/parcelboundary-pure.md new file mode 100644 index 00000000..d63d1f98 --- /dev/null +++ b/packages/dataparcels-docs/src/pages/examples/parcelboundary-pure.md @@ -0,0 +1,81 @@ +import Link from 'gatsby-link'; +import ParcelBoundaryPure from 'examples/ParcelBoundaryPure'; + +This example demonstrates nested ParcelBoundaries and their pure rendering feature. In this example, ParcelBoundaries render as coloured boxes. As you type in an input, the colours will change to indicate which ParcelBoundaries have re-rendered. + +Note how the `height` field has a prop of `pure={false}`, and therefore updates every time there is a change. + +API reference for ParcelBoundary + + + +```js +import React from 'react'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; + +const PersonParcelHoc = ParcelHoc({ + name: "personParcel", + valueFromProps: (/* props */) => ({ + name: { + first: "Robert", + last: "Clamps" + }, + age: "33", + height: "160" + }) +}); + +const DebugRender = ({children}) => { + // each render, have a new, random background colour + let rand = () => Math.floor((Math.random() * 0.75 + 0.25) * 256); + let style = { + backgroundColor: `rgb(${rand()},${rand()},${rand()})`, + padding: "1rem", + marginBottom: "1rem" + }; + return
{children}
; +}; + +const PersonEditor = (props) => { + let {personParcel} = props; + return
+ + + {(name) => + + + {(first) => + + } + + + + + {(last) => + + } + + + } + + + + + {(age) => + + } + + + + + {(height) => + + } + +
; +}; + +export default PersonParcelHoc(PersonEditor); + +``` diff --git a/packages/dataparcels-docs/src/pages/examples/parcelhoc-delayuntil.md b/packages/dataparcels-docs/src/pages/examples/parcelhoc-delayuntil.md index 3491f876..04efb354 100644 --- a/packages/dataparcels-docs/src/pages/examples/parcelhoc-delayuntil.md +++ b/packages/dataparcels-docs/src/pages/examples/parcelhoc-delayuntil.md @@ -9,7 +9,7 @@ This example shows how to delay the creation of a Parcel with `ParcelHoc`. The e ```js import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; const DelayParcelHoc = ParcelHoc({ name: "delayParcel", diff --git a/packages/dataparcels-docs/src/pages/examples/parcelhoc-example.md b/packages/dataparcels-docs/src/pages/examples/parcelhoc-example.md index a8afdf4a..a8578bfd 100644 --- a/packages/dataparcels-docs/src/pages/examples/parcelhoc-example.md +++ b/packages/dataparcels-docs/src/pages/examples/parcelhoc-example.md @@ -9,7 +9,7 @@ This example demonstrates a simple usage of `ParcelHoc`. ```js import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; const WordParcelHoc = ParcelHoc({ name: "wordParcel", diff --git a/packages/dataparcels-docs/src/pages/examples/parcelhoc-onchange.md b/packages/dataparcels-docs/src/pages/examples/parcelhoc-onchange.md index 018d5394..358d41e1 100644 --- a/packages/dataparcels-docs/src/pages/examples/parcelhoc-onchange.md +++ b/packages/dataparcels-docs/src/pages/examples/parcelhoc-onchange.md @@ -11,7 +11,7 @@ This example demonstrates a `ParcelHoc` with an initial value that originates fr ```js import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; const WordParcelHoc = ParcelHoc({ name: "wordParcel", diff --git a/packages/dataparcels-docs/src/pages/examples/parcelhoc-updatefromprops.md b/packages/dataparcels-docs/src/pages/examples/parcelhoc-updatefromprops.md index cd31edec..809906c2 100644 --- a/packages/dataparcels-docs/src/pages/examples/parcelhoc-updatefromprops.md +++ b/packages/dataparcels-docs/src/pages/examples/parcelhoc-updatefromprops.md @@ -28,7 +28,7 @@ When you type in the second input, the ParcelHoc changes and notifies the higher ```js import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; const NameParcelHoc = ParcelHoc({ name: "nameParcel", @@ -86,8 +86,8 @@ The same method can be used to allow a ParcelHoc to be controlled by another oth ```js import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; -import {ParcelBoundary} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; import ReactRouterQueryStringHoc from 'react-cool-storage/lib/ReactRouterQueryStringHoc'; import composeWith from 'unmutable/lib/util/composeWith'; diff --git a/packages/dataparcels-docs/src/pages/examples/parcelhoc-valuefromprops.md b/packages/dataparcels-docs/src/pages/examples/parcelhoc-valuefromprops.md index 315679e9..eb93416b 100644 --- a/packages/dataparcels-docs/src/pages/examples/parcelhoc-valuefromprops.md +++ b/packages/dataparcels-docs/src/pages/examples/parcelhoc-valuefromprops.md @@ -9,7 +9,7 @@ This example demonstrates a `ParcelHoc` with an initial value that originates fr ```js import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; const WordParcelHoc = ParcelHoc({ name: "wordParcel", diff --git a/packages/dataparcels-docs/src/pages/getting-started.md b/packages/dataparcels-docs/src/pages/getting-started.md index 8b0298e0..eb32d492 100644 --- a/packages/dataparcels-docs/src/pages/getting-started.md +++ b/packages/dataparcels-docs/src/pages/getting-started.md @@ -1,8 +1,6 @@ import Link from 'gatsby-link'; -import {Grid, GridItem} from 'dcme-style'; import EditingObjectsBeginner from 'examples/EditingObjectsBeginner'; import EditingObjects from 'examples/EditingObjects'; -import Examples from 'content/Examples.md'; # Getting Started @@ -43,7 +41,7 @@ We could do something like this. ```js import React from 'react'; -import {ParcelHoc} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; // PLEASE DON'T USE THIS CODE // THIS CODE IS FOR DEMONSTRAION PURPOSES ONLY @@ -85,7 +83,7 @@ export default PersonParcelHoc(PersonEditor); ### What's going on * `react-dataparcels` is imported. -* It stores the data in a `ParcelHoc` higher order component, which creates and stores a parcel in state, and passes it down as props. The parcel contains the data. +* It stores the data in a ParcelHoc higher order component, which creates and stores a parcel in state, and passes it down as props. The parcel contains the data. * The `.get()` method is used to branch off and create smaller parcels containing just `firstname`, `lastname` and `postcode`. * The `value` and the `onChangeDOM` functions aree given to each of the `input` elements to bind them to the parcel. @@ -99,7 +97,8 @@ This is the same example with a few improvements added: better rendering perform ```js import React from 'react'; -import {ParcelHoc, ParcelBoundary} from 'react-dataparcels'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; const PersonParcelHoc = ParcelHoc({ name: "personParcel", @@ -138,13 +137,10 @@ export default PersonParcelHoc(PersonEditor); ### What's better about it? -* It's now using the `ParcelBoundary` React component to make sure that inputs are only re-rendered if their values have changed. This isn't *required*, but it is **very** recommended. Without this, all inputs will re-render any time any data changes. +* It's now using the ParcelBoundary React component to make sure that inputs are only re-rendered if their values have changed. This isn't *required*, but it is **very** recommended. Without this, all inputs will re-render any time any data changes. * `.spreadDOM()` is used to provide the `value` and `onChangeDOM` props to the `input` elements more easily. -## Docs +## More -For more info see the documentation for Parcel, ParcelHoc and ParcelBoundary. - -## More examples - - +- Browse the API +- Continue on to data editing diff --git a/packages/dataparcels-docs/src/pages/index.js b/packages/dataparcels-docs/src/pages/index.js index c58d9c84..6dc92c52 100644 --- a/packages/dataparcels-docs/src/pages/index.js +++ b/packages/dataparcels-docs/src/pages/index.js @@ -8,6 +8,7 @@ import PageLayout from 'component/PageLayout'; import API from 'content/API'; import IconParcel from 'content/parcelinverted.gif'; import APIExamples from 'content/APIExamples.md'; +import APINavigation from 'component/APINavigation'; export default () => @@ -35,21 +36,23 @@ export default () => } - nav={() => - What is it? - Getting Started - Examples - API - API Examples - } + nav={() => + + What is it? + Getting Started + + + Features + - Data editing + - UI behaviour + - Data synchronisation + + + } /> API - API Examples - - - diff --git a/packages/dataparcels-docs/src/pages/index.md b/packages/dataparcels-docs/src/pages/index.md index 9a4d47d6..b3e22d95 100644 --- a/packages/dataparcels-docs/src/pages/index.md +++ b/packages/dataparcels-docs/src/pages/index.md @@ -1,7 +1,6 @@ import Link from 'gatsby-link'; import API from 'content/API'; import {Divider, Grid, GridItem, Text} from 'dcme-style'; -import Examples from 'content/Examples.md'; ## What is it? @@ -12,12 +11,45 @@ You can trigger changes to small parts of your data, and those changes will prop It's designed for use with [React](https://reactjs.org/), and comes with components for easy state management and performant rendering. The heirarchical, componentized nature of React fits perfectly with the heirarchical, componentized nature of dataparcels. -See an example of dataparcels code in action. +See an example of dataparcels code in action. + + ## Getting Started Get started with dataparcels, installation instructions and a first example. -## Examples + + +## Features + +### 1. Data editing + +Data editing is the ability to manipulate data based on user input, in a way that's expressive to code. This includes: +- Data traversal +- Binding data to inputs +- Merging partial changes into larger data structures +- Methods for working with indexed data types such as arrays +- Automatic unique keying of array elements +- Ability to modify data to fit the UI +- Managing your own Parcel state + +### 2. UI behaviour + +UI behaviour covers features that help the user interact with the data. This includes: +- Submit buttons for forms +- Validation on user input +- Confirmation on important actions such as deleting +- Selections of one or more items +- Drag and drop sorting of arrays of items +- Debouncing changes for good application performace +- Pure rendering for good rendering performace + +### 3. Data synchronisation + +Data synchronisation is how dataparcels interacts with related pieces of external data. This includes: +- Setting up a Parcel as a slave to higher state, such as the query string +- Coping with sending failable changes, such as saving data to a server +- Caching unsaved changes - + diff --git a/packages/dataparcels-docs/src/pages/parcel-keys.js b/packages/dataparcels-docs/src/pages/parcel-keys.js index 5c2f78ba..d0d07926 100644 --- a/packages/dataparcels-docs/src/pages/parcel-keys.js +++ b/packages/dataparcels-docs/src/pages/parcel-keys.js @@ -1,10 +1,11 @@ // @flow +import type {Node} from 'react'; import React from 'react'; -import {Box, CenteredLanding, Text} from 'dcme-style'; +import {Wrapper, Text, Typography} from 'dcme-style'; +import Markdown from 'pages/parcel-keys.md'; +import PageLayout from 'component/PageLayout'; -export default () => - Parcel Keys} - bottom={() => This doesn't exist yet. These docs are still very much under construction.} - /> -; +export default () => } +/>; diff --git a/packages/dataparcels-docs/src/pages/parcel-keys.md b/packages/dataparcels-docs/src/pages/parcel-keys.md new file mode 100644 index 00000000..ea16b373 --- /dev/null +++ b/packages/dataparcels-docs/src/pages/parcel-keys.md @@ -0,0 +1,35 @@ +# Parcel Keys + +Dataparcels automatically gives unique keys to all children of a parent parcel. These can safely be used as unique identifiers for each child parcel, regardless of the child parcel's position within the parent. + +## Keyed parents + +Child Parcels who belong to a keyed data type, such as an object, are given keys that match the keys on the parent data type. + +```js +let value = { + abc: 123, + def: 456 +}; + +let parcel = new Parcel({value}); + +parcel.get('abc').key // key is 'abc' +parcel.get('def').key // key is 'def' +``` + +## Indexed parents + +Child Parcels who belong to a indexed data type, such as an array, are given autogenerated keys when the Parcel is first instanciated. + +```js +let value = [ + 123, + 456 +] + +let parcel = new Parcel({value}); + +parcel.get(0).key // key is '#a' +parcel.get(1).key // key is '#b' +``` diff --git a/packages/dataparcels-docs/src/pages/parcel-meta.js b/packages/dataparcels-docs/src/pages/parcel-meta.js index ed4fdb08..78f90256 100644 --- a/packages/dataparcels-docs/src/pages/parcel-meta.js +++ b/packages/dataparcels-docs/src/pages/parcel-meta.js @@ -1,10 +1,11 @@ // @flow +import type {Node} from 'react'; import React from 'react'; -import {Box, CenteredLanding, Text} from 'dcme-style'; +import {Wrapper, Text, Typography} from 'dcme-style'; +import Markdown from 'pages/parcel-meta.md'; +import PageLayout from 'component/PageLayout'; -export default () => - Parcel Meta} - bottom={() => This doesn't exist yet. These docs are still very much under construction.} - /> -; +export default () => } +/>; diff --git a/packages/dataparcels-docs/src/pages/parcel-meta.md b/packages/dataparcels-docs/src/pages/parcel-meta.md new file mode 100644 index 00000000..30036ae2 --- /dev/null +++ b/packages/dataparcels-docs/src/pages/parcel-meta.md @@ -0,0 +1,181 @@ +import Link from 'component/Link'; +import {Link as HtmlLink} from 'dcme-style'; +import ParcelMetaConfirmingDeletions from 'examples/ParcelMetaConfirmingDeletions'; +import ParcelMetaChangedValues from 'examples/ParcelMetaChangedValues'; + +# Parcel Meta + +Parcel meta provides the ability to store extra data that pertains to parts of a data shape, such as validation error messages against each of the fields in a form. Each location (unique key path) in a Parcel's value has a corresponding meta object, which defaults to an empty object. + +```js +let value = { + abc: 123, + def: { + ghi: 456 + } +}; + +let parcel = new Parcel({value}); + +// the value above has four locations where meta can be stored: +parcel.meta // returns the meta data object on the top level parcel +parcel.get('abc').meta // returns the meta data object at ['abc'] +parcel.get('def').meta // returns the meta data object at ['def'] +parcel.get('def').get('ghi').meta // returns the meta data object at ['def', 'ghi'] +``` + +There are three main ways to interact with meta on a Parcel: +* The [meta](/api/Parcel#meta) property +* The [setMeta()](/api/Parcel#setMeta) method +* The [initialMeta()](/api/Parcel#initialMeta) method + +Meta is set using a Parcel's [setMeta()](/api/Parcel#setMeta) method, which triggers a change that sets `meta` at the parcel's location. + +```js +let parcel = new Parcel({ + value: "abc" +}); + +parcel.setMeta({ + abc: 123 +}); +// ^ this triggers a change that sets the parcel's meta to {abc: 123} + +parcel.setMeta({ + def: 456 +}); +// ^ this triggers a change that sets the parcel's meta to {abc: 123, def: 456} +``` + +The [initialMeta()](/api/Parcel#initialMeta) method can also be used to set the initial meta object. + +```js +let parcel = new Parcel({ + value: "abc" +}); + +parcel + .initialMeta({ + abc: 123 + }) + .meta // this returns {abc: 123} initially, but this can change after subsequent calls to setMeta() +``` + +# Examples + +Here are some examples of how meta can be useful. + +## Confirming deletions + +This example shows how to uses meta stored against each element in an array to show a confirmation message with options. + + + +```js +import React from 'react'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; +import ExampleHoc from 'component/ExampleHoc'; + +const FruitListParcelHoc = ParcelHoc({ + name: "fruitListParcel", + valueFromProps: (/* props */) => [ + "Apple", + "Banana", + "Crumpets" + ] +}); + +const FruitListEditor = (props) => { + let {fruitListParcel} = props; + return
+ {fruitListParcel.toArray((fruitParcel) => { + return + {(parcel) =>
+ + {parcel.meta.confirming + ? Are you sure? + + + + : } +
} +
; + })} + +
; +}; + +export default FruitListParcelHoc(FruitListEditor); +``` + +### What's going on +* Clicking on an "x" button sets the `meta.confirming` state to `true`, which renders a choice of two buttons. +* "No" sets `meta.confirming` back to false again, while "Yes" calls [delete()](/api/Parcel#delete) method on the Parcel. +* Notice how the meta always relates to the correct element, even if other elements are deleted. + +## Displaying changed values + +This example shows how `initialMeta` can be used to store information for later reference. + + + +```js +import React from 'react'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; + +const PersonParcelHoc = ParcelHoc({ + name: "personParcel", + valueFromProps: (/* props */) => ({ + firstname: "Robert", + lastname: "Clamps" + }) +}); + +const withOriginalMeta = (parcel) => parcel.initialMeta({ + original: parcel.value +}); + +const PersonEditor = (props) => { + let {personParcel} = props; + + let firstname = personParcel + .get('firstname') + .pipe(withOriginalMeta); + + let lastname = personParcel + .get('lastname') + .pipe(withOriginalMeta); + + return
+ + + {(firstname) =>
+ +
Changed? {firstname.meta.original === firstname.value ? 'No' : 'Yes'}
+
} +
+ + + + {(lastname) =>
+ +
Changed? {lastname.meta.original === lastname.value ? 'No' : 'Yes'}
+
} +
+
; +}; + +export default PersonParcelHoc(PersonEditor); + +``` + +### What's going on +* The `firstname` and `lastname` parcels use the [pipe()](/api/Parcel#pipe) method, which simply passes each parcel through the `withOriginalMeta` function and calls the [initialMeta()](/api/Parcel#initialMeta) function on each of them. +* `initialMeta()` gets the initial value of the parcel and stores it in `meta.original` +* When rendering, `meta.original` is compared against the current `value` to detect changes of the value since the initial render. + +## Validation messages on forms + +... diff --git a/packages/dataparcels-docs/src/pages/parcel-types.md b/packages/dataparcels-docs/src/pages/parcel-types.md index b1f49e38..7a33333e 100644 --- a/packages/dataparcels-docs/src/pages/parcel-types.md +++ b/packages/dataparcels-docs/src/pages/parcel-types.md @@ -8,6 +8,8 @@ Any parcel can be more than one type at once. You can check a parcel's type by using their type methods. +Parcel's can contain any type of value you like, but only a subset of these values will give you the ability to edit child values. A parcel with the ability to edit child values will be of type ParentParcel. + - ParentParcel - ChildParcel - IndexedParcel @@ -16,7 +18,7 @@ You can check a parcel's type by using their branch methods and parent methods. ### ChildParcel -A parcel is a child parcel if it contains a child value from a parent parcel. Child parcels are created using branching methods. +A parcel is a `ChildParcel` if it contains a child value from a parent parcel. Child parcels are created using branching methods. When a parcel is a child parcel, it allows the use of child methods. ### IndexedParcel -A parcel is an indexed parcel if it contains an indexed data type, such as an array or an Immutable.js List. IndexedParcels are also always parent parcels. +A parcel is an `IndexedParcel` if it contains an indexed data type, such as an array or an Immutable.js List. IndexedParcels are also always parent parcels. When a parcel is an indexed parcel, it allows the use of indexed methods. ### ElementParcel -A parcel is an element parcel if it contains the child value of an indexed parcel. +A parcel is an `ElementParcel` if it contains the child value of an indexed parcel. When a parcel is an element parcel, it allows the use of element methods. ### TopLevelParcel -A parcel is a top level parcel if it is not a child parcel. Examples: the parcel provided by a ParcelHoc, or a parcel created with `new Parcel`. +A parcel is a `TopLevelParcel` if it is not a child parcel. Examples: the parcel provided by a ParcelHoc, or a parcel created with `new Parcel`. diff --git a/packages/dataparcels-docs/src/pages/sandbox.js b/packages/dataparcels-docs/src/pages/sandbox.js new file mode 100644 index 00000000..f697fa10 --- /dev/null +++ b/packages/dataparcels-docs/src/pages/sandbox.js @@ -0,0 +1,11 @@ +// @flow +import type {Node} from 'react'; +import React from 'react'; +import {Wrapper, Text, Typography} from 'dcme-style'; +import Markdown from 'pages/sandbox.md'; +import PageLayout from 'component/PageLayout'; + +export default () => } +/>; diff --git a/packages/dataparcels-docs/src/pages/sandbox.md b/packages/dataparcels-docs/src/pages/sandbox.md new file mode 100644 index 00000000..8a1b05f3 --- /dev/null +++ b/packages/dataparcels-docs/src/pages/sandbox.md @@ -0,0 +1,8 @@ +import Link from 'gatsby-link'; +import SandboxFrame from 'sandbox/SandboxFrame'; + +# Sandbox + +## Frames + + diff --git a/packages/dataparcels-docs/src/pages/ui-behaviour.js b/packages/dataparcels-docs/src/pages/ui-behaviour.js new file mode 100644 index 00000000..c3b8bc5d --- /dev/null +++ b/packages/dataparcels-docs/src/pages/ui-behaviour.js @@ -0,0 +1,11 @@ +// @flow +import type {Node} from 'react'; +import React from 'react'; +import {Wrapper, Text, Typography} from 'dcme-style'; +import Markdown from 'pages/ui-behaviour.md'; +import PageLayout from 'component/PageLayout'; + +export default () => } +/>; diff --git a/packages/dataparcels-docs/src/pages/ui-behaviour.md b/packages/dataparcels-docs/src/pages/ui-behaviour.md new file mode 100644 index 00000000..9e0db37f --- /dev/null +++ b/packages/dataparcels-docs/src/pages/ui-behaviour.md @@ -0,0 +1,22 @@ +import Link from 'gatsby-link'; +import EditingObjects from 'examples/EditingObjects'; +import EditingArrays from 'examples/EditingArrays'; + +# UI Behaviour + +UI behaviour covers features that help the user interact with the data. + +## Submit buttons + +## Validation on user input + +## Confirmation + +## Selections + +## Drag and drop sorting + +## Debouncing changes + +## Pure rendering + diff --git a/packages/dataparcels-docs/src/sandbox/SandboxFrame.jsx b/packages/dataparcels-docs/src/sandbox/SandboxFrame.jsx new file mode 100644 index 00000000..9ccbd36a --- /dev/null +++ b/packages/dataparcels-docs/src/sandbox/SandboxFrame.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import ParcelHoc from 'react-dataparcels/ParcelHoc'; +import ParcelBoundary from 'react-dataparcels/ParcelBoundary'; +import ExampleHoc from 'component/ExampleHoc'; + +const ExampleParcelHoc = ParcelHoc({ + name: "exampleParcel", + valueFromProps: (/* props */) => ({ + abc: 123, + def: 123 + }) +}); + +const ExampleEditor = ({exampleParcel}) => { + return
+

Frames: {exampleParcel._frame}

+ + {(parcel, {release}) =>
+

Frames: {parcel._frame}

+ + +
} +
+ + + {(parcel) => } + +
; +}; + +export default ExampleParcelHoc(ExampleHoc(ExampleEditor)); diff --git a/packages/dataparcels/Action.js b/packages/dataparcels/Action.js new file mode 100644 index 00000000..7da07dc3 --- /dev/null +++ b/packages/dataparcels/Action.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('./lib/change/Action.js').default; diff --git a/packages/dataparcels/CancelActionMarker.js b/packages/dataparcels/CancelActionMarker.js new file mode 100644 index 00000000..a2df6dd0 --- /dev/null +++ b/packages/dataparcels/CancelActionMarker.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('./lib/change/CancelActionMarker.js').default; diff --git a/packages/dataparcels/ChangeRequest.js b/packages/dataparcels/ChangeRequest.js new file mode 100644 index 00000000..048aa28c --- /dev/null +++ b/packages/dataparcels/ChangeRequest.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('./lib/change/ChangeRequest.js').default; diff --git a/packages/dataparcels/DeletedParcelMarker.js b/packages/dataparcels/DeletedParcelMarker.js new file mode 100644 index 00000000..4bbc19cf --- /dev/null +++ b/packages/dataparcels/DeletedParcelMarker.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('./lib/parcelData/DeletedParcelMarker.js').default; diff --git a/packages/dataparcels/ParcelShape.js b/packages/dataparcels/ParcelShape.js new file mode 100644 index 00000000..0e77735e --- /dev/null +++ b/packages/dataparcels/ParcelShape.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('./lib/parcelShape/ParcelShape.js').default; diff --git a/packages/dataparcels/__test__/Exports-test.js b/packages/dataparcels/__test__/Exports-test.js new file mode 100644 index 00000000..35c467bd --- /dev/null +++ b/packages/dataparcels/__test__/Exports-test.js @@ -0,0 +1,49 @@ +// @flow + +// exports +import Parcel from '../src/index'; +import Action from '../Action'; +import ChangeRequest from '../ChangeRequest'; +import DeletedParcelMarker from '../DeletedParcelMarker'; +import ParcelShape from '../ParcelShape'; +import shape from '../shape'; +import CancelActionMarker from '../CancelActionMarker'; + +// internal files +import InternalParcel from '../src/parcel/Parcel'; + +// internal lib files +import InternalAction from '../lib/change/Action'; +import InternalChangeRequest from '../lib/change/ChangeRequest'; +import InternalDeletedParcelMarker from '../lib/parcelData/DeletedParcelMarker'; +import InternalParcelShape from '../lib/parcelShape/ParcelShape'; +import InternalShape from '../lib/parcelShape/shape'; +import InternalCancelActionMarker from '../lib/change/CancelActionMarker'; + +test('index should export Parcel', () => { + expect(Parcel).toBe(InternalParcel); +}); + +test('/Action should export Action', () => { + expect(Action).toBe(InternalAction); +}); + +test('/ChangeRequest should export ChangeRequest', () => { + expect(ChangeRequest).toBe(InternalChangeRequest); +}); + +test('/DeletedParcelMarker should export DeletedParcelMarker', () => { + expect(DeletedParcelMarker).toBe(InternalDeletedParcelMarker); +}); + +test('/ParcelShape should export ParcelShape', () => { + expect(ParcelShape).toBe(InternalParcelShape); +}); + +test('/shape should export shape', () => { + expect(shape).toBe(InternalShape); +}); + +test('/CancelActionMarker should export CancelActionMarker', () => { + expect(CancelActionMarker).toBe(InternalCancelActionMarker); +}); diff --git a/packages/dataparcels/package.json b/packages/dataparcels/package.json index 96456053..140af98d 100644 --- a/packages/dataparcels/package.json +++ b/packages/dataparcels/package.json @@ -1,6 +1,6 @@ { "name": "dataparcels", - "version": "0.17.2", + "version": "0.18.0-2", "description": "A library for editing data structures that works really well with React.", "main": "lib/index.js", "license": "UNLICENSED", @@ -17,13 +17,17 @@ }, "private": false, "scripts": { - "build": "rm -rf lib && NODE_ENV=production babel src --out-dir lib --ignore **/*-test.js", + "build": "rm -rf lib && NODE_ENV=production babel src --out-dir lib --ignore '**/__test__/*.js'", "build-all": "yarn build", + "size": "size-limit", "watch": "yarn run build -w" }, "dependencies": { "babel-runtime": "6.23.0", "escape-string-regexp": "^1.0.5", - "unmutable": "^0.39.0" + "unmutable": "^0.41.1" + }, + "devDependencies": { + "size-limit": "^0.21.1" } } diff --git a/packages/dataparcels/shape.js b/packages/dataparcels/shape.js new file mode 100644 index 00000000..29aa9d1d --- /dev/null +++ b/packages/dataparcels/shape.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('./lib/parcelShape/shape.js').default; diff --git a/packages/dataparcels/src/change/Action.js b/packages/dataparcels/src/change/Action.js index fb28f365..663b6ab7 100644 --- a/packages/dataparcels/src/change/Action.js +++ b/packages/dataparcels/src/change/Action.js @@ -1,41 +1,81 @@ // @flow -import type { - Key, - Index -} from '../types/Types'; +import type {Index} from '../types/Types'; +import type {Key} from '../types/Types'; + +import unshift from 'unmutable/lib/unshift'; +import update from 'unmutable/lib/update'; + +import ActionKeyPathModifier from './ActionKeyPathModifier'; type ActionData = { type?: string, payload?: Object, - keyPath?: Array + keyPath?: Array, + keyPathModifiers?: Array +}; + +type CreateActionData = { + type?: string, + payload?: Object, + keyPath?: Array, + keyPathModifiers?: Array }; export default class Action { type: string = ""; payload: Object = {}; keyPath: Array = []; + keyPathModifiers: Array; + + constructor({type, payload, keyPath, keyPathModifiers}: ActionData = {}) { - constructor(actionData: ActionData = {}) { - this.type = actionData.type || this.type; - this.payload = actionData.payload || this.payload; - this.keyPath = actionData.keyPath || this.keyPath; + if(keyPathModifiers) { + this.keyPathModifiers = keyPathModifiers; + } else if(!this.keyPathModifiers && keyPath) { + this.keyPathModifiers = keyPath.map(key => new ActionKeyPathModifier({key})); + } else { + this.keyPathModifiers = []; + } + + this.type = type || this.type; + this.payload = payload || this.payload; + this.keyPath = keyPath || this.keyPath; } - _unget = (key: Key|Index): Action => { - let {type, payload, keyPath} = this; + _create = (create: CreateActionData): Action => { return new Action({ - type, - payload, - keyPath: [key, ...keyPath] + ...this.toJS(), + ...create }); }; - shouldBeSynchronous = (): boolean => { - return this.type === "ping"; + _add = (updater: Function): Action => { + let fn = this.keyPathModifiers.length === 0 + ? unshift(updater(new ActionKeyPathModifier())) + : update(0, updater); + + return this._create({ + keyPathModifiers: fn(this.keyPathModifiers) + }); + }; + + _unget = (key: Key): Action => { + return this._create({ + keyPathModifiers: unshift(new ActionKeyPathModifier()._addKey(key))(this.keyPathModifiers), + keyPath: unshift(key)(this.keyPath) + }); + }; + + _addPre = (pre: Function): Action => { + return this._add(_ => _._addPre(pre)); + }; + + _addPost = (post: Function): Action => { + return this._add(_ => _._addPost(post)); }; isValueAction = (): boolean => { - return this.type !== "ping" && this.type !== "setMeta"; + return this.type !== "setMeta"; }; isMetaAction = (): boolean => { @@ -43,7 +83,7 @@ export default class Action { }; toJS = (): ActionData => { - let {type, payload, keyPath} = this; - return {type, payload, keyPath}; + let {type, payload, keyPath, keyPathModifiers} = this; + return {type, payload, keyPath, keyPathModifiers}; }; } diff --git a/packages/dataparcels/src/change/ActionCreators.js b/packages/dataparcels/src/change/ActionCreators.js index b0d4cb41..852f33d5 100644 --- a/packages/dataparcels/src/change/ActionCreators.js +++ b/packages/dataparcels/src/change/ActionCreators.js @@ -1,6 +1,7 @@ // @flow import type {Key} from '../types/Types'; import type {Index} from '../types/Types'; +import type {ParcelData} from '../types/Types'; import Action from './Action'; @@ -48,17 +49,30 @@ const insertBeforeSelf: Function = (value: *): Action => { }); }; -const ping: Function = (): Action => { +const move: Function = (keyA: Key|Index, keyB: Key|Index): Action => { return new Action({ - type: "ping" + type: "move", + keyPath: [keyA], + payload: { + moveKey: keyB + } }); }; -const push: Function = (value: *): Action => { +const moveSelf: Function = (keyB: Key|Index): Action => { + return new Action({ + type: "move", + payload: { + moveKey: keyB + } + }); +}; + +const push: Function = (values: Array<*>): Action => { return new Action({ type: "push", payload: { - value + values } }); }; @@ -69,6 +83,13 @@ const pop: Function = (): Action => { }); }; +const setData: Function = (parcelData: ParcelData): Action => { + return new Action({ + type: "setData", + payload: parcelData + }); +}; + const setMeta: Function = (meta: *): Action => { return new Action({ type: "setMeta", @@ -138,11 +159,11 @@ const swapSelf: Function = (keyB: Key|Index): Action => { }); }; -const unshift: Function = (value: *): Action => { +const unshift: Function = (values: Array<*>): Action => { return new Action({ type: "unshift", payload: { - value + values } }); }; @@ -153,9 +174,11 @@ export default { insertAfterSelf, insertBefore, insertBeforeSelf, - ping, + move, + moveSelf, push, pop, + setData, setMeta, setSelf, shift, diff --git a/packages/dataparcels/src/change/ActionKeyPathModifier.js b/packages/dataparcels/src/change/ActionKeyPathModifier.js new file mode 100644 index 00000000..816304d6 --- /dev/null +++ b/packages/dataparcels/src/change/ActionKeyPathModifier.js @@ -0,0 +1,55 @@ +// @flow +import type {Index} from '../types/Types'; +import type {Key} from '../types/Types'; + +import push from 'unmutable/lib/push'; +import unshift from 'unmutable/lib/unshift'; + +type ActionKeyPathModifierData = { + key?: Key|Index, + pre?: Function[], + post?: Function[] +}; + +export default class ActionKeyPathModifier { + key: ?Key|Index; + pre: Function[] = []; + post: Function[] = []; + + constructor({key, pre, post}: ActionKeyPathModifierData = {}) { + this.key = key !== undefined ? key : this.key; + this.pre = pre || this.pre; + this.post = post || this.post; + } + + _create = (create: ActionKeyPathModifierData): ActionKeyPathModifier => { + // $FlowFixMe + return new ActionKeyPathModifier({ + ...this.toJS(), + ...create + }); + }; + + _addKey = (key: Key|Index): ActionKeyPathModifier => { + return this._create({ + key + }); + }; + + _addPre = (pre: Function): ActionKeyPathModifier => { + return this._create({ + pre: unshift(pre)(this.pre) + }); + }; + + _addPost = (post: Function): ActionKeyPathModifier => { + return this._create({ + post: push(post)(this.post) + }); + }; + + toJS = (): any => { + let {key, pre, post} = this; + return {key, pre, post}; + }; +} diff --git a/packages/dataparcels/src/change/CancelActionMarker.js b/packages/dataparcels/src/change/CancelActionMarker.js new file mode 100644 index 00000000..1e39688d --- /dev/null +++ b/packages/dataparcels/src/change/CancelActionMarker.js @@ -0,0 +1,18 @@ +// @flow +import type {ParcelData} from '../types/Types'; + +const CANCELLED_ACTION_MARKER = Symbol('CANCELLED_ACTION_MARKER'); +const CANCELLED_ERROR_MESSAGE = 'CANCELLED_ERROR_MESSAGE'; + +export default CANCELLED_ACTION_MARKER; + +export const checkCancellation = (parcelData: ParcelData): ParcelData => { + if(parcelData.value === CANCELLED_ACTION_MARKER) { + throw new Error(CANCELLED_ERROR_MESSAGE); + } + return parcelData; +}; + +export const isCancelledError = (e: Error): boolean => { + return e.message === CANCELLED_ERROR_MESSAGE; +}; diff --git a/packages/dataparcels/src/change/ChangeRequest.js b/packages/dataparcels/src/change/ChangeRequest.js index 9a414e0e..40f4ed78 100644 --- a/packages/dataparcels/src/change/ChangeRequest.js +++ b/packages/dataparcels/src/change/ChangeRequest.js @@ -7,7 +7,7 @@ import type Action from './Action'; import {ReadOnlyError} from '../errors/Errors'; import {ChangeRequestUnbasedError} from '../errors/Errors'; -import Reducer from '../change/Reducer'; +import ChangeRequestReducer from '../change/ChangeRequestReducer'; import parcelGet from '../parcelData/get'; import pipe from 'unmutable/lib/util/pipe'; @@ -37,12 +37,24 @@ export default class ChangeRequest { return changeRequest; }; - _unget = (key: Key|Index): ChangeRequest => { + _createMapActions = (updater: Function): ChangeRequest => { return this._create({ - actions: this._actions.map(ii => ii._unget(key)) + actions: this._actions.map(updater) }); }; + _unget = (key: Key): ChangeRequest => { + return this._createMapActions(ii => ii._unget(key)); + }; + + _addPre = (pre: Function): ChangeRequest => { + return this._createMapActions(ii => ii._addPre(pre)); + }; + + _addPost = (post: Function): ChangeRequest => { + return this._createMapActions(ii => ii._addPost(post)); + }; + _setBaseParcel = (baseParcel: Parcel): ChangeRequest => { return this._create({ baseParcel @@ -50,7 +62,7 @@ export default class ChangeRequest { }; // $FlowFixMe - this doesn't have side effects - get nextData(): * { + get nextData(): ?ParcelData { if(!this._baseParcel) { throw ChangeRequestUnbasedError(); } @@ -66,7 +78,7 @@ export default class ChangeRequest { .get(this._baseParcel._id.id()) .data; - let data = Reducer(parcelDataFromRegistry, this._actions); + let data = ChangeRequestReducer(this)(parcelDataFromRegistry); this._cachedData = data; return data; } @@ -77,7 +89,7 @@ export default class ChangeRequest { } // $FlowFixMe - this doesn't have side effects - get prevData(): * { + get prevData(): ParcelData { if(!this._baseParcel) { throw ChangeRequestUnbasedError(); } @@ -160,12 +172,6 @@ export default class ChangeRequest { return next.value !== prev.value; }; - shouldBeSynchronous = (): boolean => { - return this - ._actions - .some(action => action.shouldBeSynchronous()); - }; - toJS = (): Object => { return { actions: this._actions.map(action => action.toJS()), diff --git a/packages/dataparcels/src/change/ChangeRequestReducer.js b/packages/dataparcels/src/change/ChangeRequestReducer.js new file mode 100644 index 00000000..78147a59 --- /dev/null +++ b/packages/dataparcels/src/change/ChangeRequestReducer.js @@ -0,0 +1,119 @@ +// @flow +import type ChangeRequest from './ChangeRequest'; +import type Action from './Action'; +import type {ParcelData} from '../types/Types'; +import type {ParcelDataEvaluator} from '../types/Types'; + +import identity from 'unmutable/lib/identity'; +import last from 'unmutable/lib/last'; +import update from 'unmutable/lib/update'; +import pipe from 'unmutable/lib/util/pipe'; +import pipeWith from 'unmutable/lib/util/pipeWith'; +import composeWith from 'unmutable/lib/util/composeWith'; + +import {ReducerInvalidActionError} from '../errors/Errors'; +import {isCancelledError} from './CancelActionMarker'; + +import del from '../parcelData/delete'; +import deleteSelfWithMarker from '../parcelData/deleteSelfWithMarker'; +import insertAfter from '../parcelData/insertAfter'; +import insertBefore from '../parcelData/insertBefore'; +import move from '../parcelData/move'; +import pop from '../parcelData/pop'; +import push from '../parcelData/push'; +import setMeta from '../parcelData/setMeta'; +import setSelf from '../parcelData/setSelf'; +import shift from '../parcelData/shift'; +import swap from '../parcelData/swap'; +import swapNext from '../parcelData/swapNext'; +import swapPrev from '../parcelData/swapPrev'; +import unshift from '../parcelData/unshift'; +import parcelDataUpdate from '../parcelData/update'; + +const actionMap = { + delete: ({lastKey}) => del(lastKey), + insertAfter: ({lastKey, value}) => insertAfter(lastKey, value), + insertBefore: ({lastKey, value}) => insertBefore(lastKey, value), + move: ({lastKey, moveKey}) => move(lastKey, moveKey), + pop: () => pop(), + push: ({values}) => push(...values), + setData: parcelData => () => parcelData, + setMeta: ({meta}) => setMeta(meta), + set: ({value}) => setSelf(value), + shift: () => shift(), + swap: ({lastKey, swapKey}) => swap(lastKey, swapKey), + swapNext: ({lastKey}) => swapNext(lastKey), + swapPrev: ({lastKey}) => swapPrev(lastKey), + unshift: ({values}) => unshift(...values) +}; + +const parentActionMap = { + delete: true, + insertAfter: true, + insertBefore: true, + move: true, + swap: true, + swapNext: true, + swapPrev: true +}; + +const doAction = ({keyPath, type, payload}: Action): ParcelDataEvaluator => { + let fn = actionMap[type]; + if(!fn) { + throw ReducerInvalidActionError(type); + } + return fn({ + ...payload, + lastKey: last()(keyPath) + }); +}; + +const doDeepAction = (action: Action): ParcelDataEvaluator => { + let {keyPathModifiers, type} = action; + let isParentAction: boolean = !!(parentActionMap[type]); + + if(isParentAction) { + if(action.keyPath.length === 0) { + return type === "delete" ? deleteSelfWithMarker : identity(); + } + keyPathModifiers = pipeWith( + keyPathModifiers, + update(-1, keyPathModifier => keyPathModifier._addKey(null)) + // ^ if isParentAction set last keyPathModifier to null so that + // next() is called instead of parcelDataUpdate(key, next) + ); + } + + return composeWith( + ...keyPathModifiers.map(({key, pre, post}) => (next) => pipe( + ...pre, + (key || key === 0) ? parcelDataUpdate(key, next) : next, + ...post, + )), + doAction(action) + ); +}; + +export default (changeRequest: ChangeRequest) => (parcelData: ParcelData): ?ParcelData => { + let cancelled = 0; + let actions = changeRequest.actions(); + + let newParcelData = pipeWith( + parcelData, + ...actions.map((action): ParcelDataEvaluator => (parcelData: ParcelData): ParcelData => { + try { + return doDeepAction(action)(parcelData); + } catch(e) { + if(isCancelledError(e)) { + cancelled++; + return parcelData; + } + throw e; + } + }) + ); + + return cancelled > 0 && cancelled === actions.length + ? undefined + : newParcelData; +}; diff --git a/packages/dataparcels/src/change/Reducer.js b/packages/dataparcels/src/change/Reducer.js deleted file mode 100644 index 28fc7b14..00000000 --- a/packages/dataparcels/src/change/Reducer.js +++ /dev/null @@ -1,175 +0,0 @@ -// @flow -import type {Index} from '../types/Types'; -import type {Key} from '../types/Types'; -import type {ParcelData} from '../types/Types'; - -import {ReducerInvalidActionError} from '../errors/Errors'; -import {ReducerSwapKeyError} from '../errors/Errors'; - -import butLast from 'unmutable/lib/butLast'; -import isEmpty from 'unmutable/lib/isEmpty'; -import last from 'unmutable/lib/last'; - -import parcelDelete from '../parcelData/delete'; -import parcelDeleteSelfWithMarker from '../parcelData/deleteSelfWithMarker'; -import parcelInsert from '../parcelData/insert'; - -import parcelPop from '../parcelData/pop'; -import parcelPush from '../parcelData/push'; -import parcelSetMeta from '../parcelData/setMeta'; -import parcelSetSelf from '../parcelData/setSelf'; -import parcelShift from '../parcelData/shift'; -import parcelSwap from '../parcelData/swap'; -import parcelSwapNextPrev from '../parcelData/swapNextPrev'; -import parcelUnshift from '../parcelData/unshift'; -import parcelUpdateIn from '../parcelData/updateIn'; - -import Action from './Action'; - -export default function MultiReducer(parcelData: ParcelData, action: Action|Action[]): ParcelData { - let actionArray: Action[] = Array.isArray(action) ? action : [action]; - let reduced: ParcelData = actionArray.reduce(Reducer, parcelData); - return { - value: undefined, - ...reduced - }; -} - -function Reducer(parcelData: ParcelData, action: Action|Action[]): ParcelData { - if(!(action instanceof Action)) { - throw Error(`Reducer must receive an Action`); - } - - let { - keyPath, - payload: { - value, - meta - }, - type - } = action; - - let keyPathLast: Key|Index = last()(keyPath); - let keyPathButLast: Array = butLast()(keyPath); - let keyPathIsEmpty: boolean = isEmpty()(keyPath); - - let updateIn = (keyPath, updater) => parcelUpdateIn(keyPath, updater)(parcelData); - - switch(type) { - case "delete": { - if(keyPathIsEmpty) { - return parcelDeleteSelfWithMarker(); - } - return updateIn( - keyPathButLast, - parcelDelete(keyPathLast) - ); - } - - case "insertAfter": { - if(keyPathIsEmpty) { - return parcelData; - } - return updateIn( - keyPathButLast, - parcelInsert(keyPathLast, value, true) - ); - } - - case "insertBefore": { - if(keyPathIsEmpty) { - return parcelData; - } - return updateIn( - keyPathButLast, - parcelInsert(keyPathLast, value, false) - ); - } - - case "ping": { - return parcelData; - } - - case "pop": { - return updateIn( - keyPath, - parcelPop() - ); - } - - case "push": { - return updateIn( - keyPath, - parcelPush(value) - ); - } - - case "set": { - return updateIn( - keyPath, - parcelSetSelf(value, false) - ); - - } - - case "setMeta": { - return updateIn( - keyPath, - parcelSetMeta(meta) - ); - } - - case "shift": { - return updateIn( - keyPath, - parcelShift() - ); - } - - case "swap": { - if(keyPathIsEmpty) { - return parcelData; - } - let {swapKey} = action.payload; - if(typeof swapKey === "undefined") { - throw ReducerSwapKeyError(); - } - - return updateIn( - keyPathButLast, - parcelSwap(keyPathLast, swapKey) - ); - } - - case "swapNext": { - if(keyPathIsEmpty) { - return parcelData; - } - - return updateIn( - keyPathButLast, - parcelSwapNextPrev(keyPathLast, true) - ); - } - - case "swapPrev": { - if(keyPathIsEmpty) { - return parcelData; - } - - return updateIn( - keyPathButLast, - parcelSwapNextPrev(keyPathLast, false) - ); - } - - case "unshift": { - return updateIn( - keyPath, - parcelUnshift(value) - ); - } - } - - throw ReducerInvalidActionError(type); -} diff --git a/packages/dataparcels/src/change/__test__/Action-test.js b/packages/dataparcels/src/change/__test__/Action-test.js index a081b10b..de5cd8c0 100644 --- a/packages/dataparcels/src/change/__test__/Action-test.js +++ b/packages/dataparcels/src/change/__test__/Action-test.js @@ -1,65 +1,234 @@ // @flow import Action from '../Action'; +import ActionKeyPathModifier from '../ActionKeyPathModifier'; test('Action should build an action', () => { let expectedDefaultData = { type: "", payload: {}, - keyPath: [] + keyPath: [], + keyPathModifiers: [] }; - expect(expectedDefaultData).toEqual(new Action().toJS()); + expect(new Action().toJS()).toEqual(expectedDefaultData); }); test('Action should build an action with a type', () => { - expect("???").toBe(new Action({type: "???"}).type); + expect(new Action({type: "???"}).type).toBe("???"); }); test('Action should build an action with a payload', () => { - expect({a: 1}).toEqual(new Action({type: "???", payload: {a: 1}}).payload); + expect(new Action({type: "???", payload: {a: 1}}).payload).toEqual({a: 1}); }); test('Action should build an action with a default payload', () => { - expect({}).toEqual(new Action({type: "???"}).payload); + expect(new Action({type: "???"}).payload).toEqual({}); }); test('Action should build an action with a keyPath', () => { - expect(['a', 'b']).toEqual(new Action({type: "???", keyPath: ['a', 'b']}).keyPath); + expect(new Action({type: "???", keyPath: ['a', 'b']}).keyPath).toEqual(['a', 'b']); }); test('Action should build an action with a default keyPath', () => { - expect([]).toEqual(new Action({type: "???"}).keyPath); + expect(new Action({type: "???"}).keyPath).toEqual([]); }); -test('Action should be synchronous if it has a type of ping, else not', () => { - expect(new Action({type: "ping"}).shouldBeSynchronous()).toBe(true); - expect(new Action({type: "set"}).shouldBeSynchronous()).toBe(false); - expect(new Action({type: "???"}).shouldBeSynchronous()).toBe(false); +test('Action _unget should unshift key to front of empty keyPath', () => { + let action = { + type: "woo", + payload: {abc: 123}, + keyPath: [], + keyPathModifiers: [] + }; + + let {keyPath, keyPathModifiers} = new Action(action) + ._unget('a') + .toJS(); + + expect(keyPath).toEqual(['a']); + expect(keyPathModifiers.map(_ => _.toJS())).toEqual([ + {key: "a", pre: [], post: []} + ]); }); -test('Action should unshift key to front of keyPath when ungetting', () => { - let action = { +test('Action _unget should unshift key to front of keyPath', () => { + let action = { type: "woo", payload: {abc: 123}, keyPath: ['a'] }; - let expectedAction = { + let {keyPath, keyPathModifiers} = new Action(action) + ._unget('b') + .toJS(); + + expect(keyPath).toEqual(['b', 'a']); + expect(keyPathModifiers.map(_ => _.toJS())).toEqual([ + {key: "b", pre: [], post: []}, + {key: "a", pre: [], post: []} + ]); +}); + +test('Action _unget should unshift key into new keypathmodifier where pre already exists', () => { + let fn = ii => ii; + + let action = { + type: "woo", + payload: {abc: 123}, + keyPath: [], + keyPathModifiers: [ + new ActionKeyPathModifier({key: undefined, pre: [fn]}) + ] + }; + + let {keyPath, keyPathModifiers} = new Action(action) + ._unget('a') + .toJS(); + + expect(keyPath).toEqual(['a']); + expect(keyPathModifiers.map(_ => _.toJS())).toEqual([ + {key: "a", pre: [], post: []}, + {key: undefined, pre: [fn], post: []} + ]); +}); + +test('Action _addPre and _addPost should put pre and post functions in empty keyPathModifiers', () => { + let action = { + type: "woo", + payload: {abc: 123}, + keyPath: [] + }; + + let pre = ii => ii; + let post = ii => ii; + + let withPre = new Action(action) + ._addPre(pre) + .toJS() + .keyPathModifiers; + + let withPost = new Action(action) + ._addPost(post) + .toJS() + .keyPathModifiers; + + expect(withPre.map(_ => _.toJS())).toEqual([ + {key: undefined, pre: [pre], post: []} + ]); + + expect(withPost.map(_ => _.toJS())).toEqual([ + {key: undefined, post: [post], pre: []} + ]); +}); + +test('Action _addPre and _addPost should put pre and post functions into first keyPathModifiers', () => { + let action = { + type: "woo", + payload: {abc: 123}, + keyPath: ['a'], + keyPathModifiers: [ + new ActionKeyPathModifier({key: 'a'}) + ] + }; + + let pre = ii => ii; + let post = ii => ii; + + let withPre = new Action(action) + ._addPre(pre) + .toJS() + .keyPathModifiers; + + let withPost = new Action(action) + ._addPost(post) + .toJS() + .keyPathModifiers; + + expect(withPre.map(_ => _.toJS())).toEqual([ + {key: 'a', pre: [pre], post: []} + ]); + + expect(withPost.map(_ => _.toJS())).toEqual([ + {key: 'a', pre: [], post: [post]} + ]); +}); + +test('Action _addPre and _addPost should put pre and post functions in same keyPathModifiers', () => { + let action = { + type: "woo", + payload: {abc: 123}, + keyPath: ['a'], + keyPathModifiers: [ + new ActionKeyPathModifier({key: 'a'}) + ] + }; + + let pre = ii => ii; + let post = ii => ii; + + let withPreAndPost = new Action(action) + ._addPre(pre) + ._addPost(post) + .toJS() + .keyPathModifiers; + + expect(withPreAndPost.map(_ => _.toJS())).toEqual([ + {key: 'a', post: [post], pre: [pre]} + ]); +}); + +test('Action _addPre twice should put pre functions in same keyPathModifiers, in reverse order', () => { + let action = { type: "woo", payload: {abc: 123}, - keyPath: ['b', 'a'] + keyPath: ['a'], + keyPathModifiers: [ + new ActionKeyPathModifier({key: 'a'}) + ] }; - expect(expectedAction).toEqual(new Action(action)._unget('b').toJS()); + let pre = ii => ii; + let pre2 = ii => ii; + + let withPre = new Action(action) + ._addPre(pre) + ._addPre(pre2) + .toJS() + .keyPathModifiers; + + expect(withPre.map(_ => _.toJS())).toEqual([ + {key: 'a', post: [], pre: [pre2, pre]} + ]); }); -test('Action should be value action if it isnt ping or setMeta', () => { - expect(new Action({type: "ping"}).isValueAction()).toBe(false); +test('Action _addPost twice should put post functions in same keyPathModifiers', () => { + let action = { + type: "woo", + payload: {abc: 123}, + keyPath: ['a'], + keyPathModifiers: [ + new ActionKeyPathModifier({key: 'a'}) + ] + }; + + let post = ii => ii; + let post2 = ii => ii; + + let withPost = new Action(action) + ._addPost(post) + ._addPost(post2) + .toJS() + .keyPathModifiers; + + expect(withPost.map(_ => _.toJS())).toEqual([ + {key: 'a', post: [post, post2], pre: []} + ]); +}); +test('Action should be value action if it isnt setMeta', () => { expect(new Action({type: "set"}).isValueAction()).toBe(true); expect(new Action({type: "setMeta"}).isValueAction()).toBe(false); }); test('Action should be meta action if it is setMeta', () => { - expect(new Action({type: "ping"}).isMetaAction()).toBe(false); expect(new Action({type: "set"}).isMetaAction()).toBe(false); expect(new Action({type: "setMeta"}).isMetaAction()).toBe(true); }); diff --git a/packages/dataparcels/src/change/__test__/ChangeRequest-test.js b/packages/dataparcels/src/change/__test__/ChangeRequest-test.js index 2fcd4336..79cd10a8 100644 --- a/packages/dataparcels/src/change/__test__/ChangeRequest-test.js +++ b/packages/dataparcels/src/change/__test__/ChangeRequest-test.js @@ -52,8 +52,6 @@ test('ChangeRequest merge() should merge other change requests actions', () => { let merged = a.merge(b); expect([...actionsA, ...actionsB]).toEqual(merged.actions()); - - // TODO - test originId, originPath and meta }); @@ -198,31 +196,6 @@ test('ChangeRequest should keep originId and originPath', () => { new Parcel(data) .get('abc') .onChange(456); - //.modifyChangeValue(value => value + 1) -}); - - -test('ChangeRequest should keep originId and originPath even when going through a batch() where another change is fired before the original one', () => { - expect.assertions(2); - - var data = { - value: { - abc: 123, - def: 456 - }, - handleChange: (parcel: Parcel, changeRequest: ChangeRequest) => { - expect(['abc']).toEqual(changeRequest.originPath); - expect('^.~mcb-1049856033.abc').toEqual(changeRequest.originId); - } - }; - - new Parcel(data) - .modifyChangeBatch((parcel, changeRequest) => { - parcel.set('def', 789); - parcel.dispatch(changeRequest); - }) - .get('abc') - .onChange(456); }); test('ChangeRequest should cache its data after its calculated, so subsequent calls are faster', () => { @@ -310,39 +283,6 @@ test('ChangeRequest should cache its data after its calculated, so subsequent ca expect(ms2).toBeLessThan(ms / 100); // expect amazing performance boosts from having cached }); -test('ChangeRequest data chache should be invalidated correctly', () => { - expect.assertions(6); - - var parcel = new Parcel({ - value: { - a: { - b: 123 - } - } - }); - - parcel - .get('a') - .modifyChangeBatch((parcel, changeRequest) => { - expect(changeRequest.nextData).toEqual({key: 'a', meta: {abc: 123}, value: {b: 456}, child: {b:{key: "b"}}}); - expect(changeRequest.nextData).toEqual({key: 'a', meta: {abc: 123}, value: {b: 456}, child: {b:{key: "b"}}}); // get cached - parcel.dispatch(changeRequest); - }) - .modifyChangeBatch((parcel, changeRequest) => { - expect(changeRequest.nextData).toEqual({key: 'a', meta: {}, value: {b: 456}, child: {b:{key: "b"}}}); - expect(changeRequest.nextData).toEqual({key: 'a', meta: {}, value: {b: 456}, child: {b:{key: "b"}}}); // get cached - parcel.dispatch(changeRequest); - parcel.setMeta({abc: 123}); - }) - .get('b') - .modifyChangeBatch((parcel, changeRequest) => { - expect(changeRequest.nextData).toEqual({key: 'b', meta: {}, value: 456}); - expect(changeRequest.nextData).toEqual({key: 'b', meta: {}, value: 456}); // get cached - parcel.dispatch(changeRequest); - }) - .onChange(456); -}); - test('ChangeRequest getDataIn should return previous and next value at keyPath', () => { var action = new Action({ type: "set", diff --git a/packages/dataparcels/src/change/__test__/ChangeRequestReducer-test.js b/packages/dataparcels/src/change/__test__/ChangeRequestReducer-test.js new file mode 100644 index 00000000..9418f6e9 --- /dev/null +++ b/packages/dataparcels/src/change/__test__/ChangeRequestReducer-test.js @@ -0,0 +1,266 @@ +// @flow +import ActionCreators from '../ActionCreators'; +import ChangeRequest from '../ChangeRequest'; +import ChangeRequestReducer from '../ChangeRequestReducer'; +import Action from '../Action'; + +import push from 'unmutable/lib/push'; +import update from 'unmutable/lib/update'; +import pipeWith from 'unmutable/lib/util/pipeWith'; + +const makeReducer = (actions) => pipeWith( + new ChangeRequest(actions), + ChangeRequestReducer +); +/* +test('ChangeRequestReducer should pass through with no actions', () => { + var data = { + value: 123, + key: "^", + child: undefined + }; + + let actions = []; + + expect(makeReducer(actions)(data)).toEqual(data); +}); + +test('ChangeRequestReducer should process a single "set" action', () => { + var data = { + value: 123, + key: "^", + child: undefined + }; + + let actions = [ + ActionCreators.setSelf(456) + ]; + + let expectedData = { + ...data, + value: 456 + }; + + expect(makeReducer(actions)(data)).toEqual(expectedData); +}); + +test('ChangeRequestReducer should process two "set" actions', () => { + var data = { + value: 123, + key: "^", + child: undefined + }; + + let actions = [ + ActionCreators.setSelf(456), + ActionCreators.setSelf(789) + ]; + + let expectedData = { + ...data, + value: 789 + }; + + expect(makeReducer(actions)(data)).toEqual(expectedData); +}); + +test('ChangeRequestReducer should process single "push" action', () => { + var data = { + value: ["A"], + key: "^", + child: undefined + }; + + let actions = [ + ActionCreators.push("B") + ]; + + let expectedData = { + ...data, + value: ["A","B"], + child: [{key: '#a'}, {key: '#b'}] + }; + + expect(makeReducer(actions)(data)).toEqual(expectedData); +}); + +test('ChangeRequestReducer should process single "push" action with pre functions', () => { + var data = { + value: ["A"], + key: "^", + child: undefined + }; + + let pushValue = (newValue) => update('value', push(newValue)) + let pre1 = jest.fn(pushValue("1")); + let pre2 = jest.fn(pushValue("2")); + + let actions = [ + ActionCreators + .push("B") + ._addPre(pre1) + ._addPre(pre2) + ]; + + let expectedData = { + ...data, + value: ["A","2","1","B"], + child: [{key: '#a'}, {key: '#b'}, {key: '#c'}, {key: '#d'}] + }; + + expect(makeReducer(actions)(data)).toEqual(expectedData); +}); + +test('ChangeRequestReducer should process single "push" action with post functions', () => { + var data = { + value: ["A"], + key: "^", + child: undefined + }; + + let pushValue = (newValue) => update('value', push(newValue)) + let post1 = jest.fn(pushValue("1")); + let post2 = jest.fn(pushValue("2")); + + let actions = [ + ActionCreators + .push("B") + ._addPost(post1) + ._addPost(post2) + ]; + + let expectedData = { + ...data, + value: ["A","B","1","2"], + child: [{key: '#a'}, {key: '#b'}] + }; + + expect(makeReducer(actions)(data)).toEqual(expectedData); +}); + +test('ChangeRequestReducer should process a single "set" action at a depth of 1', () => { + var data = { + value: {abc: 123}, + key: "^", + child: undefined + }; + + let actions = [ + ActionCreators + .setSelf(456) + ._unget('abc') + ]; + + let expectedData = { + ...data, + value: {abc: 456}, + child: { + abc: { + child: undefined, + key: "abc" + } + } + }; + + expect(makeReducer(actions)(data)).toEqual(expectedData); +}); + +test('ChangeRequestReducer should process a single "set" action at a depth of 2', () => { + var data = { + value: { + abc: { + def: 123 + } + }, + key: "^", + child: undefined + }; + + let actions = [ + ActionCreators + .setSelf(456) + ._unget('def') + ._unget('abc') + ]; + + let expectedData = { + ...data, + value: { + abc: { + def: 456 + } + }, + child: { + abc: { + child: { + def: { + child: undefined, + key: "def" + } + }, + key: "abc" + } + } + }; + + expect(makeReducer(actions)(data)).toEqual(expectedData); +}); + +test('ChangeRequestReducer should process aa complicated bunch of pre and post functions', () => { + var data = { + value: {abc: 123}, + key: "^", + child: undefined + }; + + let actions = [ + ActionCreators + .setSelf(456) + ._addPost(update('value', value => value + 1)) // 456 -> 457 + ._unget('abc') + ._addPost(update('value', value => ({...value, alsoAlso: value.abc}))) // {abc: 457, also: 788} -> {abc: 457, also: 788, alsoAlso: 457} + ._addPre(update('value', update('also', also => also - 1))) // {abc: 123} -> {abc: 123, also: 788} + ._addPre(update('value', value => ({...value, also: 789}))) // {abc: 123} -> {abc: 123, also: 789} + // wierdly, looking at this syntax, the value starts at the bottom and goes up + ]; + + let expectedValue = { + abc: 457, + also: 788, + alsoAlso: 457 + }; + + expect(makeReducer(actions)(data).value).toEqual(expectedValue); +});*/ + +test('ChangeRequestReducer should process pre and post on parentActions like "swapNextSelf"', () => { + var data = { + value: "abc.def.ghi", + key: "^" + }; + + let pre = update('value', value => value.split(".")); + let post = update('value', value => value.join(".")); + + let actions = [ + ActionCreators + .swapNextSelf() + ._unget(0) + ._addPre(pre) + ._addPost(post) + ]; + + let expectedData = { + key: "^", + value: "def.abc.ghi" + }; + + let { + child, // throw away child as this is normally dealt with in updaters + // it is the reducers job to execute actions correctly, not to ensure the integrity of the data + // or protect against the setting of invalid data shapes + ...processed + } = makeReducer(actions)(data); + + expect(processed).toEqual(expectedData); +}); diff --git a/packages/dataparcels/src/change/__test__/ReducerDelete-test.js b/packages/dataparcels/src/change/__test__/ChangeRequestReducerDelete-test.js similarity index 60% rename from packages/dataparcels/src/change/__test__/ReducerDelete-test.js rename to packages/dataparcels/src/change/__test__/ChangeRequestReducerDelete-test.js index 16ae340a..31d700ca 100644 --- a/packages/dataparcels/src/change/__test__/ReducerDelete-test.js +++ b/packages/dataparcels/src/change/__test__/ChangeRequestReducerDelete-test.js @@ -1,9 +1,16 @@ // @flow -import Reducer from '../Reducer'; +import ChangeRequest from '../ChangeRequest'; +import ChangeRequestReducer from '../ChangeRequestReducer'; import Action from '../Action'; import DeletedParcelMarker from '../../parcelData/DeletedParcelMarker'; +import pipeWith from 'unmutable/lib/util/pipeWith'; -test('Reducer should delete key', () => { +const makeReducer = (action) => pipeWith( + new ChangeRequest(action), + ChangeRequestReducer +); + +test('ChangeRequestReducer should delete key', () => { var data = { value: { a: 1, @@ -21,10 +28,10 @@ test('Reducer should delete key', () => { b: 2 }; - expect(Reducer(data, action).value).toEqual(expectedValue); + expect(makeReducer(action)(data).value).toEqual(expectedValue); }); -test('Reducer should delete deep key', () => { +test('ChangeRequestReducer should delete deep key', () => { var data = { value: { a: { @@ -48,10 +55,10 @@ test('Reducer should delete deep key', () => { c: 3 }; - expect(Reducer(data, action).value).toEqual(expectedValue); + expect(makeReducer(action)(data).value).toEqual(expectedValue); }); -test('Reducer should set value to DeletedParcelMarker symbol if deleted with no keypath', () => { +test('ChangeRequestReducer should set value to DeletedParcelMarker symbol if deleted with no keypath', () => { var data = { value: { a: 1, @@ -69,5 +76,5 @@ test('Reducer should set value to DeletedParcelMarker symbol if deleted with no value: DeletedParcelMarker }; - expect(Reducer(data, action)).toEqual(expectedData); + expect(makeReducer(action)(data)).toEqual(expectedData); }); diff --git a/packages/dataparcels/src/change/__test__/ReducerIndexed-test.js b/packages/dataparcels/src/change/__test__/ChangeRequestReducerIndexed-test.js similarity index 89% rename from packages/dataparcels/src/change/__test__/ReducerIndexed-test.js rename to packages/dataparcels/src/change/__test__/ChangeRequestReducerIndexed-test.js index 68f3d3bd..dd80a70d 100644 --- a/packages/dataparcels/src/change/__test__/ReducerIndexed-test.js +++ b/packages/dataparcels/src/change/__test__/ChangeRequestReducerIndexed-test.js @@ -1,7 +1,13 @@ // @flow - -import Reducer from '../Reducer'; +import ChangeRequest from '../ChangeRequest'; +import ChangeRequestReducer from '../ChangeRequestReducer'; import Action from '../Action'; +import pipeWith from 'unmutable/lib/util/pipeWith'; + +const makeReducer = (action) => pipeWith( + new ChangeRequest(action), + ChangeRequestReducer +); let data = { value: [0,1,2], @@ -17,27 +23,19 @@ let EXPECTED_KEY_AND_META = { [ "insertAfter", "insertBefore", + "move", "swap", "swapNext", "swapPrev" ].forEach((type: string) => { test(`Reducer ${type} action should return unchanged parcelData if keyPath is empty`, () => { - expect(Reducer(data, new Action({type}))).toEqual(data); + expect(makeReducer(new Action({type}))(data)).toEqual(data); }); }); -test('Reducer swap action should throw error if payload.swapKey doesnt exist', () => { - let action = new Action({ - type: "swap", - keyPath: [0] - }); - - expect(() => Reducer(data, action)).toThrowError(`swap actions must have a swapKey in their payload`); -}); - const TestIndex = (arr) => arr.map(({action, expectedData}) => { test(`Reducer ${action.type} action should ${action.type} with keyPath ${JSON.stringify(action.keyPath)}`, () => { - expect(Reducer(data, new Action(action))).toEqual(expectedData); + expect(makeReducer(new Action(action))(data)).toEqual(expectedData); }); let deepAction = { @@ -75,7 +73,7 @@ const TestIndex = (arr) => arr.map(({action, expectedData}) => { }; test(`Reducer ${action.type} action should ${action.type} deeply with keyPath ${JSON.stringify(deepAction.keyPath)}`, () => { - expect(Reducer(deepData, new Action(deepAction))).toEqual(deepExpectedData); + expect(makeReducer(new Action(deepAction))(deepData)).toEqual(deepExpectedData); }); }); @@ -233,6 +231,23 @@ TestIndex([ } ]); +TestIndex([ + { + action: { + type: "move", + payload: { + moveKey: 0 + }, + keyPath: [2] + }, + expectedData: { + value: [2,0,1], + child: [{key: "#c"},{key: "#a"}, {key: "#b"}], + ...EXPECTED_KEY_AND_META + } + } +]); + TestIndex([ { action: { @@ -253,12 +268,12 @@ TestIndex([ type: "push", keyPath: [], payload: { - value: 3 + values: [3,4] } }, expectedData: { - value: [0,1,2,3], - child: [{key: "#a"},{key: "#b"}, {key: "#c"},{key: "#d"}], + value: [0,1,2,3,4], + child: [{key: "#a"},{key: "#b"}, {key: "#c"},{key: "#d"},{key: "#e"}], ...EXPECTED_KEY_AND_META } } @@ -424,12 +439,12 @@ TestIndex([ type: "unshift", keyPath: [], payload: { - value: 3 + values: [3,4] } }, expectedData: { - value: [3,0,1,2], - child: [{key: "#d"},{key: "#a"},{key: "#b"}, {key: "#c"}], + value: [3,4,0,1,2], + child: [{key: "#d"},{key: "#e"},{key: "#a"},{key: "#b"}, {key: "#c"}], ...EXPECTED_KEY_AND_META } } diff --git a/packages/dataparcels/src/change/__test__/ReducerSet-test.js b/packages/dataparcels/src/change/__test__/ChangeRequestReducerSet-test.js similarity index 83% rename from packages/dataparcels/src/change/__test__/ReducerSet-test.js rename to packages/dataparcels/src/change/__test__/ChangeRequestReducerSet-test.js index 524e5dfb..1ae27916 100644 --- a/packages/dataparcels/src/change/__test__/ReducerSet-test.js +++ b/packages/dataparcels/src/change/__test__/ChangeRequestReducerSet-test.js @@ -1,8 +1,15 @@ // @flow -import Reducer from '../Reducer'; +import ChangeRequest from '../ChangeRequest'; +import ChangeRequestReducer from '../ChangeRequestReducer'; import Action from '../Action'; +import pipeWith from 'unmutable/lib/util/pipeWith'; -test('Reducer should set with empty keyPath', () => { +const makeReducer = (action) => pipeWith( + new ChangeRequest(action), + ChangeRequestReducer +); + +test('ChangeRequestReducer should set with empty keyPath', () => { var data = { value: 123, meta: { @@ -27,10 +34,10 @@ test('Reducer should set with empty keyPath', () => { // value should be replaced // key and meta should be untouched - expect(Reducer(data, action)).toEqual(expectedData); + expect(makeReducer(action)(data)).toEqual(expectedData); }); -test('Reducer should set with empty keyPath and clear existing child', () => { +test('ChangeRequestReducer should set with empty keyPath and clear existing child', () => { var data = { value: { a: { @@ -75,10 +82,10 @@ test('Reducer should set with empty keyPath and clear existing child', () => { // value should be replaced // key and meta should be untouched // child should be removed - expect(Reducer(data, action)).toEqual(expectedData); + expect(makeReducer(action)(data)).toEqual(expectedData); }); -test('Reducer should set with keyPath of 1 element', () => { +test('ChangeRequestReducer should set with keyPath of 1 element', () => { var data = { value: { a: 1, @@ -127,10 +134,10 @@ test('Reducer should set with keyPath of 1 element', () => { // value should be replaced at keypath // key and meta should be untouched // top level child should be kept - expect(Reducer(data, action)).toEqual(expectedData); + expect(makeReducer(action)(data)).toEqual(expectedData); }); -test('Reducer should clear child from set key', () => { +test('ChangeRequestReducer should clear child from set key', () => { var data = { value: { a: { @@ -192,10 +199,10 @@ test('Reducer should clear child from set key', () => { // child should be removed at keyPath // top level key and meta should be untouched // child.b.meta should be untouched - expect(Reducer(data, action)).toEqual(expectedData); + expect(makeReducer(action)(data)).toEqual(expectedData); }); -test('Reducer should set with keyPath of 2 elements', () => { +test('ChangeRequestReducer should set with keyPath of 2 elements', () => { var data = { value: { a: {}, @@ -251,10 +258,10 @@ test('Reducer should set with keyPath of 2 elements', () => { // value should be replaced at keypath // key and meta should be untouched // top level child should be kept - expect(Reducer(data, action)).toEqual(expectedData); + expect(makeReducer(action)(data)).toEqual(expectedData); }); -test('Reducer should set with keyPath of 2 elements on arrays', () => { +test('ChangeRequestReducer should set with keyPath of 2 elements on arrays', () => { var data = { value: [ [], @@ -307,10 +314,10 @@ test('Reducer should set with keyPath of 2 elements on arrays', () => { // key and meta should be untouched // top level child should be kept // keys should be generated for existing value, and for newly set value - expect(Reducer(data, action)).toEqual(expectedData); + expect(makeReducer(action)(data)).toEqual(expectedData); }); -test('Reducer should set with an unkeyed array and give it keys', () => { +test('ChangeRequestReducer should set with an unkeyed array and give it keys', () => { var data = { value: 9, meta: { @@ -351,10 +358,10 @@ test('Reducer should set with an unkeyed array and give it keys', () => { // key and meta should be untouched // top level child should be kept // keys should be generated for newly set value - expect(Reducer(data, action)).toEqual(expectedData); + expect(makeReducer(action)(data)).toEqual(expectedData); }); -test('Reducer should set (with a keyPath) with an unkeyed array and give it keys', () => { +test('ChangeRequestReducer should set (with a keyPath) with an unkeyed array and give it keys', () => { var data = { value: { a: [0,0,0] @@ -401,5 +408,5 @@ test('Reducer should set (with a keyPath) with an unkeyed array and give it keys // key and meta should be untouched // top level child should be kept // keys should be generated for newly set value - expect(Reducer(data, action)).toEqual(expectedData); + expect(makeReducer(action)(data)).toEqual(expectedData); }); diff --git a/packages/dataparcels/src/change/__test__/ReducerSetMeta-test.js b/packages/dataparcels/src/change/__test__/ChangeRequestReducerSetMeta-test.js similarity index 71% rename from packages/dataparcels/src/change/__test__/ReducerSetMeta-test.js rename to packages/dataparcels/src/change/__test__/ChangeRequestReducerSetMeta-test.js index 6b57cd10..48088958 100644 --- a/packages/dataparcels/src/change/__test__/ReducerSetMeta-test.js +++ b/packages/dataparcels/src/change/__test__/ChangeRequestReducerSetMeta-test.js @@ -1,8 +1,15 @@ // @flow -import Reducer from '../Reducer'; +import ChangeRequest from '../ChangeRequest'; +import ChangeRequestReducer from '../ChangeRequestReducer'; import Action from '../Action'; +import pipeWith from 'unmutable/lib/util/pipeWith'; -test('Reducer should setMeta with empty keyPath', () => { +const makeReducer = (action) => pipeWith( + new ChangeRequest(action), + ChangeRequestReducer +); + +test('ChangeRequestReducer should setMeta with empty keyPath', () => { var data = { value: { a: 1, @@ -24,10 +31,10 @@ test('Reducer should setMeta with empty keyPath', () => { abc: 123 }; - expect(Reducer(data, action).meta).toEqual(expectedMeta); + expect(makeReducer(action)(data).meta).toEqual(expectedMeta); }); -test('Reducer should setMeta merge', () => { +test('ChangeRequestReducer should setMeta merge', () => { var data = { value: { a: 1, @@ -53,10 +60,10 @@ test('Reducer should setMeta merge', () => { def: 456 }; - expect(Reducer(data, action).meta).toEqual(expectedMeta); + expect(makeReducer(action)(data).meta).toEqual(expectedMeta); }); -test('Reducer should setMeta with keyPath', () => { +test('ChangeRequestReducer should setMeta with keyPath', () => { var data = { value: { a: 1, @@ -86,10 +93,10 @@ test('Reducer should setMeta with keyPath', () => { } }; - expect(Reducer(data, action).child).toEqual(expectedChild); + expect(makeReducer(action)(data).child).toEqual(expectedChild); }); -test('Reducer should merge setMeta with keyPath', () => { +test('ChangeRequestReducer should merge setMeta with keyPath', () => { var data = { value: { a: 1, @@ -130,6 +137,6 @@ test('Reducer should merge setMeta with keyPath', () => { } }; - expect(Reducer(data, action).child).toEqual(expectedChild); + expect(makeReducer(action)(data).child).toEqual(expectedChild); }); diff --git a/packages/dataparcels/src/change/__test__/Reducer-test.js b/packages/dataparcels/src/change/__test__/Reducer-test.js deleted file mode 100644 index 7c20d161..00000000 --- a/packages/dataparcels/src/change/__test__/Reducer-test.js +++ /dev/null @@ -1,14 +0,0 @@ -// @flow -import Reducer from '../Reducer'; -import Action from '../Action'; - -test('Reducer should throw error if action is not provided', () => { - var data = { - value: 123, - key: "^", - child: undefined - }; - - // $FlowFixMe - intential misuse of types - expect(() => Reducer(data)).toThrowError(`Reducer must receive an Action`); -}); diff --git a/packages/dataparcels/src/change/__test__/ReducerPing-test.js b/packages/dataparcels/src/change/__test__/ReducerPing-test.js deleted file mode 100644 index aa6eb0ee..00000000 --- a/packages/dataparcels/src/change/__test__/ReducerPing-test.js +++ /dev/null @@ -1,26 +0,0 @@ -// @flow -import Reducer from '../Reducer'; -import Action from '../Action'; - -test('Reducer should ping', () => { - var data = { - value: [ - 0, - 1, - 2 - ], - key: "^", - child: undefined - }; - var action = new Action({ - type: "ping" - }); - - var expectedValue = [ - 0, - 1, - 2 - ]; - expect(expectedValue).toEqual(Reducer(data, action).value); -}); - diff --git a/packages/dataparcels/src/errors/Errors.js b/packages/dataparcels/src/errors/Errors.js index dda575fb..4b1c3699 100644 --- a/packages/dataparcels/src/errors/Errors.js +++ b/packages/dataparcels/src/errors/Errors.js @@ -2,5 +2,5 @@ export const ReadOnlyError = () => new Error(`This property is read-only`); export const ReducerInvalidActionError = (actionType: string) => new Error(`"${actionType}" is not a valid action`); -export const ReducerSwapKeyError = () => new Error(`swap actions must have a swapKey in their payload`); export const ChangeRequestUnbasedError = () => new Error(`ChangeRequest data cannot be accessed before calling changeRequest._setBaseParcel()`); +export const ShapeUpdaterNonShapeChildError = () => new Error(`Every child value on a collection returned from a shape updater must be a ParcelShape`); diff --git a/packages/dataparcels/src/index.js b/packages/dataparcels/src/index.js index 19d8bc21..a3f36020 100644 --- a/packages/dataparcels/src/index.js +++ b/packages/dataparcels/src/index.js @@ -2,22 +2,26 @@ import Parcel from './parcel/Parcel'; export default Parcel; -export {default as Action} from './change/Action'; -export {default as ActionCreators} from './change/ActionCreators'; -export {default as ChangeRequest} from './change/ChangeRequest'; -export {default as DeletedParcelMarker} from './parcelData/DeletedParcelMarker'; +// +// types +// export type {ParcelData} from './types/Types'; export type {ParcelConfig} from './types/Types'; export type {ParcelConfigInternal} from './types/Types'; +export type {ParcelCreateConfigType} from './types/Types'; export type {ParcelMeta} from './types/Types'; -export type {ParcelMetaUpdater} from './types/Types'; -export type {ParcelBatcher} from './types/Types'; export type {ParcelMapper} from './types/Types'; export type {ParcelUpdater} from './types/Types'; export type {ParcelValueUpdater} from './types/Types'; +export type {ParcelShapeUpdater} from './types/Types'; + +export type {ParcelShapeSetMeta} from './types/Types'; +export type {ParcelShapeValueUpdater} from './types/Types'; +export type {ParcelShapeConfigInternal} from './types/Types'; export type {Key} from './types/Types'; export type {Index} from './types/Types'; export type {Property} from './types/Types'; +export type {ParentType} from './types/Types'; diff --git a/packages/dataparcels/src/match/Matcher.js b/packages/dataparcels/src/match/Matcher.js deleted file mode 100644 index f0dd5614..00000000 --- a/packages/dataparcels/src/match/Matcher.js +++ /dev/null @@ -1,127 +0,0 @@ -// @flow -import escapeStringRegexp from 'escape-string-regexp'; - -import join from 'unmutable/lib/join'; -import keyArray from 'unmutable/lib/keyArray'; -import map from 'unmutable/lib/map'; -import skip from 'unmutable/lib/skip'; -import pipe from 'unmutable/lib/util/pipe'; -import pipeWith from 'unmutable/lib/util/pipeWith'; - -export const split = (match: string): string[] => pipeWith( - match, - escapeSpecialChars, - ii => ii.split("."), - map(unescapeSpecialChars) -); - -export const containsWildcard = (match: string): boolean => pipeWith( - match, - escapeSpecialChars, - ii => ii.replace(/\*\*/g, "").indexOf("*") !== -1 -); - -export const containsGlobstar = (match: string): boolean => pipeWith( - match, - escapeSpecialChars, - ii => ii.indexOf("**") !== -1 -); - -const TYPE_SELECTORS = { - ["Child"]: "C", - ["!Child"]: "c", - ["Element"]: "E", - ["!Element"]: "e", - ["Indexed"]: "I", - ["!Indexed"]: "i", - ["Parent"]: "P", - ["!Parent"]: "p", - ["TopLevel"]: "T", - ["!TopLevel"]: "t" -}; - -const SPECIAL_CHARS = [".", ":", "|", "*"]; -const REGEX_SPECIAL_CHARS = SPECIAL_CHARS.map(escapeStringRegexp); -const MATCH_ANY_IN_PART = "[^.]*?"; - -const escapeSpecialChars = pipe( - ...pipeWith( - REGEX_SPECIAL_CHARS, - map((chr: string, index: number): Function => { - return str => str.replace(new RegExp(`%${chr}`, "g"), `%${index}`); - }) - ) -); - -const unescapeSpecialChars = pipe( - ...pipeWith( - SPECIAL_CHARS, - map((chr: string, index: number): Function => { - return str => str.replace(new RegExp(`%${index}`, "g"), `%${chr}`); - }) - ) -); - -const regexifyGlobstars = str => str.replace(/\\\*\\\*/g, ".*?"); -const regexifyWildcards = str => str.replace(/\\\*/g, MATCH_ANY_IN_PART); - -const regexifyPart = (part: string): string => { - let [name, type] = part.split(":"); - - if(name === "") { - name = MATCH_ANY_IN_PART; - } - - // if no type, match any type selector - if(!type) { - return `${name}:${MATCH_ANY_IN_PART}`; - } - - // replace type with type selectors - let typeSelector = TYPE_SELECTORS[type]; - if(!typeSelector) { - let choices = pipeWith( - TYPE_SELECTORS, - keyArray(), - join(", ") - ); - throw new Error(`"${type}" is not a valid type selector. Choose one of ${choices}`); - } - - return `${name}:${MATCH_ANY_IN_PART}[${typeSelector}]${MATCH_ANY_IN_PART}`; -}; - -export default (typedPathString: string, match: string, depth: number = 0): boolean => { - let test = escapeSpecialChars(typedPathString); - if(depth > 0) { - test = pipeWith( - test, - ii => ii.split("."), - skip(depth), - join(".") - ); - } - - return escapeSpecialChars(match) - .split("|") - .some((escapedMatch: string): boolean => { - if(escapedMatch[0] === ".") { - if(escapedMatch.length === 1 || escapedMatch[1] === ":") { - escapedMatch = escapedMatch.slice(1); - } - escapedMatch = `*${escapedMatch}`; - } - - return pipeWith( - escapedMatch, - escapeStringRegexp, - ii => ii.split("\\."), - map(regexifyPart), - join("\\."), - regexifyGlobstars, - regexifyWildcards, - regex => `^${regex}$`, - (regex) => new RegExp(regex, "g").test(test) - ); - }); -}; diff --git a/packages/dataparcels/src/match/__test__/Matcher-test.js b/packages/dataparcels/src/match/__test__/Matcher-test.js deleted file mode 100644 index f8774e71..00000000 --- a/packages/dataparcels/src/match/__test__/Matcher-test.js +++ /dev/null @@ -1,426 +0,0 @@ -// @flow -import Matcher, { split, containsWildcard, containsGlobstar } from '../Matcher'; - -import filter from 'unmutable/lib/filter'; -import identity from 'unmutable/lib/identity'; -import keyArray from 'unmutable/lib/keyArray'; -import map from 'unmutable/lib/map'; -import pipeWith from 'unmutable/lib/util/pipeWith'; - -// -// match -// - -let typedPathStrings = { - top: "^:ceiPT", - childValue: "^:ceiPT.abc:Ceipt", - childObject: "^:ceiPT.def:CeiPt", - grandchildValue: "^:ceiPT.def:CeiPt.defkid:Ceipt", - grandchildWithDot: "^:ceiPT.def:CeiPt.dot%.dot:Ceipt", - greatGrandchild: "^:ceiPT.def:CeiPt.more:CeiPt.abc:Ceipt", - childArray: "^:ceiPT.ghi:CeIPt", - grandchildElement: "^:ceiPT.ghi:CeIPt.#a:CEipt", - escapeChars: "^:ceiPT.jkl%.%:%|%#%,:Ceipt" -}; - -let matchTests = [ - { - name: "match self", - match: ".", - shouldMatch: ["top"] - }, - { - name: "match self with depth = 1", - match: ".", - shouldMatch: [ - "childValue", - "childObject", - "childArray", - "escapeChars" - ], - depth: 1 - }, - { - name: "match self with depth = 2", - match: ".", - shouldMatch: [ - "grandchildValue", - "grandchildWithDot", - "grandchildElement" - ], - depth: 2 - }, - { - name: "match self with depth = 3", - match: ".", - shouldMatch: [ - "greatGrandchild" - ], - depth: 3 - }, - { - name: "match by full name", - match: "^", - shouldMatch: ["top"] - }, - { - name: "match child by full name", - match: ".abc", - shouldMatch: ["childValue"] - }, - { - name: "match child with escape chars", - match: ".jkl%.%:%|%#%,", - shouldMatch: ["escapeChars"] - }, - { - name: "match child that doesnt exist / dont match grandchild", - match: ".defkid", - shouldMatch: [] - }, - { - name: "match grandchild", - match: ".def.defkid", - shouldMatch: ["grandchildValue"] - }, - { - name: "match grandchild element", - match: ".ghi.#a", - shouldMatch: ["grandchildElement"] - }, - { - name: "match multiple", - match: ".abc|.ghi.#a", - shouldMatch: ["childValue", "grandchildElement"] - }, - { - name: "match wildcard", - match: ".*", - shouldMatch: ["childValue", "childObject", "childArray", "escapeChars"] - }, - { - name: "match wildcard start", - match: ".*f", - shouldMatch: ["childObject"] - }, - { - name: "match wildcard middle", - match: ".*e*", - shouldMatch: ["childObject"] - }, - { - name: "match wildcard end", - match: ".d*", - shouldMatch: ["childObject"] - }, - { - name: "match wildcard in array", - match: ".ghi.*", - shouldMatch: ["grandchildElement"] - }, - { - name: "match wildcards in keypath", - match: ".def.*.abc", - shouldMatch: ["greatGrandchild"] - }, - { - name: "match wildcards in keypath", - match: ".*.*.*", - shouldMatch: ["greatGrandchild"] // and NOT grandchildWithDot - }, - { - name: "match globstar", - match: "**", - shouldMatch: [ - "top", - "childValue", - "childObject", - "grandchildValue", - "grandchildWithDot", - "greatGrandchild", - "childArray", - "grandchildElement", - "escapeChars" - ] - }, - { - name: "match globstar start", - match: "**abc", - shouldMatch: ["childValue", "greatGrandchild"] - }, - { - name: "match globstar start (negative test)", - match: "**def", - shouldMatch: ["childObject"] - }, - { - name: "match globstar middle", - match: "**def**", - shouldMatch: [ - "childObject", - "grandchildValue", - "grandchildWithDot", - "greatGrandchild" - ] - }, - { - name: "match globstar end", - match: ".ghi.**", - shouldMatch: ["grandchildElement"] - }, - { - name: "match globstar children", - match: ".**", - shouldMatch: [ - "childValue", - "childObject", - "grandchildValue", - "grandchildWithDot", - "greatGrandchild", - "childArray", - "grandchildElement", - "escapeChars" - ] - }, - { - name: "match self with type", - match: ".:Parent", - shouldMatch: ["top"] - }, - { - name: "match self with type", - match: ".:Indexed", - shouldMatch: [] - }, - { - name: "match child by full name with type", - match: ".abc:Child", - shouldMatch: ["childValue"] - }, - { - name: "match child by full name with type", - match: ".abc:Parent", - shouldMatch: [] - }, - { - name: "match child with escape chars with type", - match: ".jkl%.%:%|%#%,:Child", - shouldMatch: ["escapeChars"] - }, - { - name: "match child with escape chars with type", - match: ".jkl%.%:%|%#%,:Parent", - shouldMatch: [] - }, - { - name: "match wildcard children with type", - match: ".*:Child", - shouldMatch: ["childValue", "childObject", "childArray", "escapeChars"] - }, - { - name: "match wildcard children with type", - match: ".*:Parent", - shouldMatch: ["childObject", "childArray"] - }, - { - name: "match wildcard children with type", - match: ".*:Indexed", - shouldMatch: ["childArray"] - }, - { - name: "match wildcard children with type", - match: ".*:Element", - shouldMatch: [] - }, - { - name: "match wildcard children with type", - match: ".*:TopLevel", - shouldMatch: [] - }, - { - name: "match wildcard children with negative type", - match: ".*:!Child", - shouldMatch: [] - }, - { - name: "match wildcard children with negative type", - match: ".*:!Parent", - shouldMatch: ["childValue", "escapeChars"] - }, - { - name: "match wildcard children with negative type", - match: ".*:!Indexed", - shouldMatch: ["childValue", "childObject", "escapeChars"] - }, - { - name: "match wildcard children with negative type", - match: ".*:!Element", - shouldMatch: ["childValue", "childObject", "childArray", "escapeChars"] - }, - { - name: "match wildcard children with negative type", - match: ".*:!TopLevel", - shouldMatch: ["childValue", "childObject", "childArray", "escapeChars"] - }, - { - name: "match globstar with type", - match: "**:Parent", - shouldMatch: [ - "top", - "childObject", - "childArray" - ] - }, - { - name: "match globstar with wildcard", - match: "**.*", - shouldMatch: [ - "childValue", - "childObject", - "grandchildValue", - "grandchildWithDot", - "greatGrandchild", - "childArray", - "grandchildElement", - "escapeChars" - ] - }, - { - name: "match globstar with two wildcards", - match: "**.*.*", - shouldMatch: [ - "grandchildValue", - "grandchildWithDot", - "greatGrandchild", - "grandchildElement" - ] - }, - { - name: "match globstar and wildcard with type", - match: "**.*:Parent", - shouldMatch: [ - "childObject", - "childArray" - ] - }, - { - name: "match globstar with type", - match: "**:Indexed", - shouldMatch: ["childArray"] - }, - { - name: "match globstar with type", - match: "**:Element", - shouldMatch: ["grandchildElement"] - }, - { - name: "match globstar with type", - match: "**:TopLevel", - shouldMatch: ["top"] - }, - { - name: "match globstar with negative type", - match: "**:!Child", - shouldMatch: ["top"] - }, - { - name: "match globstar with negative type", - match: "**:!Parent", - shouldMatch: [ - "childValue", - "grandchildValue", - "grandchildWithDot", - "greatGrandchild", - "grandchildElement", - "escapeChars" - ] - }, - { - name: "match globstar with negative type", - match: "**:!Indexed", - shouldMatch: [ - "top", - "childValue", - "childObject", - "grandchildValue", - "grandchildWithDot", - "greatGrandchild", - "grandchildElement", - "escapeChars" - ] - }, - { - name: "match globstar with negative type", - match: "**:!Element", - shouldMatch: [ - "top", - "childValue", - "childObject", - "grandchildValue", - "grandchildWithDot", - "greatGrandchild", - "childArray", - "escapeChars" - ] - }, - { - name: "match globstar with negative type", - match: "**:!TopLevel", - shouldMatch: [ - "childValue", - "childObject", - "grandchildValue", - "grandchildWithDot", - "greatGrandchild", - "childArray", - "grandchildElement", - "escapeChars" - ] - } -]; - -pipeWith( - matchTests, - map(({name, match, matchParsed, shouldMatch, depth = 0}) => { - test(`${name}`, () => { - let matched: string[] = pipeWith( - typedPathStrings, - map((typedPathString, name) => Matcher(typedPathString, match, depth)), - filter(identity()), - keyArray() - ); - expect(matched).toEqual(shouldMatch); - }); - }) -); - -test(`split() should split`, () => { - expect(['abc']).toEqual(split(`abc`)); - expect(['abc', 'def']).toEqual(split(`abc.def`)); - expect(['abc', 'de%.f']).toEqual(split(`abc.de%.f`)); -}); - -test(`containsWildcard() should identify when a match string contains a wildcard`, () => { - expect(containsWildcard(`abc`)).toBe(false); - expect(containsWildcard(`abc*`)).toBe(true); - expect(containsWildcard(`abc.*`)).toBe(true); - expect(containsWildcard(`abc%*`)).toBe(false); - expect(containsWildcard(`abc%*.*`)).toBe(true); - expect(containsWildcard(`**`)).toBe(false); - expect(containsWildcard(`abc.**`)).toBe(false); - expect(containsWildcard(`abc.*.**`)).toBe(true); - expect(containsWildcard(`abc%**`)).toBe(true); -}); - -test(`containsGlobstar() should identify when a match string contains a globstar`, () => { - expect(containsGlobstar(`abc`)).toBe(false); - expect(containsGlobstar(`abc*`)).toBe(false); - expect(containsGlobstar(`abc.*`)).toBe(false); - expect(containsGlobstar(`abc%*`)).toBe(false); - expect(containsGlobstar(`abc%*.*`)).toBe(false); - expect(containsGlobstar(`**`)).toBe(true); - expect(containsGlobstar(`abc.**`)).toBe(true); - expect(containsGlobstar(`abc.*.**`)).toBe(true); - expect(containsGlobstar(`abc%**`)).toBe(false); -}); - diff --git a/packages/dataparcels/src/parcel/Parcel.js b/packages/dataparcels/src/parcel/Parcel.js index c35ddd90..9caf3cdf 100644 --- a/packages/dataparcels/src/parcel/Parcel.js +++ b/packages/dataparcels/src/parcel/Parcel.js @@ -1,19 +1,18 @@ // @flow import type Action from '../change/Action'; import type ChangeRequest from '../change/ChangeRequest'; -import type {CreateParcelConfigType} from '../types/Types'; +import type {ParcelCreateConfigType} from '../types/Types'; import type {Index} from '../types/Types'; import type {Key} from '../types/Types'; -import type {MatchPipe} from '../types/Types'; -import type {ParcelBatcher} from '../types/Types'; import type {ParcelConfigInternal} from '../types/Types'; import type {ParcelConfig} from '../types/Types'; import type {ParcelData} from '../types/Types'; import type {ParcelMapper} from '../types/Types'; -import type {ParcelMetaUpdater} from '../types/Types'; import type {ParcelMeta} from '../types/Types'; import type {ParcelUpdater} from '../types/Types'; import type {ParcelValueUpdater} from '../types/Types'; +import type {ParentType} from '../types/Types'; +import type {ParcelShapeUpdateFunction} from '../types/Types'; import Types from '../types/Types'; import {ReadOnlyError} from '../errors/Errors'; @@ -29,21 +28,21 @@ import ChildChangeMethods from './methods/ChildChangeMethods'; import ElementChangeMethods from './methods/ElementChangeMethods'; import ModifyMethods from './methods/ModifyMethods'; import AdvancedMethods from './methods/AdvancedMethods'; -import TopLevelMethods from './methods/TopLevelMethods'; import FilterMethods from '../util/FilterMethods'; import ParcelTypes from './ParcelTypes'; import ParcelId from '../parcelId/ParcelId'; import Treeshare from '../treeshare/Treeshare'; +import setSelf from '../parcelData/setSelf'; import overload from 'unmutable/lib/util/overload'; const DEFAULT_CONFIG_INTERNAL = () => ({ onDispatch: undefined, child: undefined, + lastOriginId: '', meta: {}, id: new ParcelId(), - matchPipes: [], parent: undefined, treeshare: undefined }); @@ -54,23 +53,22 @@ export default class Parcel { let { handleChange, - value, - debugRender = false + value } = config; handleChange && Types(`Parcel()`, `config.handleChange`, `function`)(handleChange); - Types(`Parcel()`, `config.debugRender`, `boolean`)(debugRender); let { onDispatch, child, + lastOriginId, meta, id, - matchPipes, parent, treeshare } = _configInternal || DEFAULT_CONFIG_INTERNAL(); + this._lastOriginId = lastOriginId; this._onHandleChange = handleChange; this._onDispatch = onDispatch; @@ -87,9 +85,6 @@ export default class Parcel { this._parent = parent; } - // match pipes - this._matchPipes = matchPipes || []; - // types this._parcelTypes = new ParcelTypes( value, @@ -97,13 +92,11 @@ export default class Parcel { !!(id && id.path().length === 0) ); + // id this._id = id; - if(!_configInternal || parent) { - this._id.setTypeCode(this._parcelTypes.toTypeCode()); - } // treeshare - this._treeshare = treeshare || new Treeshare({debugRender}); + this._treeshare = treeshare || new Treeshare(); this._treeshare.registry.set(id.id(), this); let dispatch = (dispatchable: Action|Action[]|ChangeRequest) => this._methods.dispatch(dispatchable); @@ -131,9 +124,7 @@ export default class Parcel { // $FlowFixMe ...ModifyMethods(this), // $FlowFixMe - ...AdvancedMethods(this), - // $FlowFixMe - ...FilterMethods("TopLevel", TopLevelMethods)(this, dispatch) + ...AdvancedMethods(this) }; } @@ -141,28 +132,30 @@ export default class Parcel { // private // + // from constructor + _id: ParcelId; + _lastOriginId: string; _methods: { [key: string]: * }; _onHandleChange: ?Function; _onDispatch: ?Function; _parcelData: ParcelData; - _id: ParcelId; - _matchPipes: MatchPipe[]; - _treeshare: Treeshare; _parcelTypes: ParcelTypes; _parent: ?Parcel; - _dispatchBuffer: ?Function; - _log: boolean = false; - _logName: string = ""; + _treeshare: Treeshare; + + // from methods + _log: boolean = false; // used by log() + _logName: string = ""; // used by log() - _create = (createParcelConfig: CreateParcelConfigType): Parcel => { + _create = (createParcelConfig: ParcelCreateConfigType): Parcel => { let { id = this._id, onDispatch = this.dispatch, handleChange, - matchPipes = this._matchPipes, parent, parcelData = this._parcelData, - treeshare = this._treeshare + treeshare = this._treeshare, + lastOriginId = this._lastOriginId } = createParcelConfig; let { @@ -171,25 +164,29 @@ export default class Parcel { meta = {} } = parcelData; - let parcel: Parcel = new Parcel( + return new Parcel( { value, handleChange }, { child, + lastOriginId, meta, id, - matchPipes, onDispatch, parent, treeshare } ); + }; - return parent - ? parcel._methods._applyMatchPipes() - : parcel; + _setAndReturn = (value: any): Parcel => { + // $FlowFixMe + return this._create({ + handleChange: this._onHandleChange, + parcelData: setSelf(value)(this._parcelData) + }); }; // @@ -202,7 +199,7 @@ export default class Parcel { } // $FlowFixMe - this doesn't have side effects - set data(value: *) { + set data(value: any) { throw ReadOnlyError(); } @@ -212,7 +209,7 @@ export default class Parcel { } // $FlowFixMe - this doesn't have side effects - set value(value: *) { + set value(value: any) { throw ReadOnlyError(); } @@ -223,7 +220,7 @@ export default class Parcel { } // $FlowFixMe - this doesn't have side effects - set meta(value: *) { + set meta(value: any) { throw ReadOnlyError(); } @@ -233,7 +230,7 @@ export default class Parcel { } // $FlowFixMe - this doesn't have side effects - set key(value: *) { + set key(value: any) { throw ReadOnlyError(); } @@ -243,7 +240,7 @@ export default class Parcel { } // $FlowFixMe - this doesn't have side effects - set id(value: *) { + set id(value: any) { throw ReadOnlyError(); } @@ -253,7 +250,7 @@ export default class Parcel { } // $FlowFixMe - this doesn't have side effects - set path(value: *) { + set path(value: any) { throw ReadOnlyError(); } @@ -262,14 +259,15 @@ export default class Parcel { // // Spread methods - spread = (notFoundValue: ?* = undefined): * => this._methods.spread(notFoundValue); - spreadDOM = (notFoundValue: ?* = undefined): * => this._methods.spreadDOM(notFoundValue); + spread = (notFoundValue: any = undefined): * => this._methods.spread(notFoundValue); + spreadDOM = (notFoundValue: any = undefined): * => this._methods.spreadDOM(notFoundValue); // Branch methods - get = (key: Key|Index, notFoundValue: ?* = undefined): Parcel => this._methods.get(key, notFoundValue); - getIn = (keyPath: Array, notFoundValue: ?* = undefined): Parcel => this._methods.getIn(keyPath, notFoundValue); - toObject = (mapper: ParcelMapper = _ => _): { [key: string]: * } => this._methods.toObject(mapper); - toArray = (mapper: ParcelMapper = _ => _): Array<*> => this._methods.toArray(mapper); + get = (key: Key|Index, notFoundValue: any = undefined): Parcel => this._methods.get(key, notFoundValue); + getIn = (keyPath: Array, notFoundValue: any = undefined): Parcel => this._methods.getIn(keyPath, notFoundValue); + children = (mapper: ParcelMapper = _ => _): ParentType => this._methods.children(mapper); + toObject = (mapper: ParcelMapper = _ => _): { [key: string]: Parcel } => this._methods.toObject(mapper); + toArray = (mapper: ParcelMapper = _ => _): Array => this._methods.toArray(mapper); // Parent methods has = (key: Key|Index): boolean => this._methods.has(key); @@ -279,26 +277,23 @@ export default class Parcel { isFirst = (): boolean => this._methods.isFirst(); isLast = (): boolean => this._methods.isLast(); - // Status methods - hasDispatched = (): boolean => this._methods.hasDispatched(); - // Side-effect methods log = (name: string = ""): Parcel => this._methods.log(name); spy = (sideEffect: Function): Parcel => this._methods.spy(sideEffect); spyChange = (sideEffect: Function): Parcel => this._methods.spyChange(sideEffect); // Change methods - onChange = (value: *) => this._methods.onChange(value); + onChange = (value: any) => this._methods.onChange(value); onChangeDOM = (event: *) => this._methods.onChangeDOM(event); set = overload({ - ["1"]: (value: *) => this._methods.setSelf(value), - ["2"]: (key: Key|Index, value: *) => this._methods.set(key, value) + ["1"]: (value: any) => this._methods.setSelf(value), + ["2"]: (key: Key|Index, value: any) => this._methods.set(key, value) }); update = overload({ - ["1"]: (updater: ParcelValueUpdater) => this._methods.updateSelf(updater), - ["2"]: (key: Key|Index, updater: ParcelValueUpdater) => this._methods.update(key, updater) + ["1"]: (updater: ParcelValueUpdater|ParcelShapeUpdateFunction) => this._methods.updateSelf(updater), + ["2"]: (key: Key|Index, updater: ParcelValueUpdater|ParcelShapeUpdateFunction) => this._methods.update(key, updater) }); - setIn = (keyPath: Array, value: *) => this._methods.setIn(keyPath, value); + setIn = (keyPath: Array, value: any) => this._methods.setIn(keyPath, value); updateIn = (keyPath: Array, updater: ParcelValueUpdater) => this._methods.updateIn(keyPath, updater); delete = overload({ ["0"]: () => this._methods.deleteSelf(), @@ -308,23 +303,22 @@ export default class Parcel { // Advanced change methods setMeta = (partialMeta: ParcelMeta) => this._methods.setMeta(partialMeta); - updateMeta = (updater: ParcelMetaUpdater) => this._methods.updateMeta(updater); - setChangeRequestMeta = (partialMeta: ParcelMeta) => this._methods.setChangeRequestMeta(partialMeta); dispatch = (dispatchable: Action|Action[]|ChangeRequest) => this._methods.dispatch(dispatchable); - batch = (batcher: ParcelBatcher, changeRequest: ?ChangeRequest) => this._methods.batch(batcher, changeRequest); - batchAndReturn = (batcher: ParcelBatcher, changeRequest: ?ChangeRequest) => this._methods.batchAndReturn(batcher, changeRequest); - ping = () => this._methods.ping(); // Indexed methods insertAfter = overload({ - ["1"]: (value: *) => this._methods.insertAfterSelf(value), - ["2"]: (key: Key|Index, value: *) => this._methods.insertAfter(key, value) + ["1"]: (value: any) => this._methods.insertAfterSelf(value), + ["2"]: (key: Key|Index, value: any) => this._methods.insertAfter(key, value) }); insertBefore = overload({ - ["1"]: (value: *) => this._methods.insertBeforeSelf(value), - ["2"]: (key: Key|Index, value: *) => this._methods.insertBefore(key, value) + ["1"]: (value: any) => this._methods.insertBeforeSelf(value), + ["2"]: (key: Key|Index, value: any) => this._methods.insertBefore(key, value) + }); + move = overload({ + ["1"]: (key: Key|Index) => this._methods.moveSelf(key), + ["2"]: (keyA: Key|Index, keyB: Key|Index) => this._methods.move(keyA, keyB) }); - push = (value: *) => this._methods.push(value); + push = (...values: Array) => this._methods.push(...values); pop = () => this._methods.pop(); shift = () => this._methods.shift(); swap = overload({ @@ -339,13 +333,12 @@ export default class Parcel { ["0"]: () => this._methods.swapPrevSelf(), ["1"]: (key: Key|Index) => this._methods.swapPrev(key) }); - unshift = (value: *) => this._methods.unshift(value); + unshift = (...values: Array) => this._methods.unshift(...values); // Modify methods - modifyValue = (updater: Function): Parcel => this._methods.modifyValue(updater); - modifyChangeBatch = (batcher: Function): Parcel => this._methods.modifyChangeBatch(batcher); - modifyChangeValue = (updater: Function): Parcel => this._methods.modifyChangeValue(updater); - initialMeta = (initialMeta: ParcelMeta = {}): Parcel => this._methods.initialMeta(initialMeta); + modifyDown = (updater: ParcelValueUpdater|ParcelShapeUpdateFunction): Parcel => this._methods.modifyDown(updater); + modifyUp = (updater: ParcelValueUpdater|ParcelShapeUpdateFunction): Parcel => this._methods.modifyUp(updater); + initialMeta = (initialMeta: ParcelMeta): Parcel => this._methods.initialMeta(initialMeta); _boundarySplit = (config: *): Parcel => this._methods._boundarySplit(config); // Type methods @@ -357,7 +350,6 @@ export default class Parcel { // Composition methods pipe = (...updaters: ParcelUpdater[]): Parcel => this._methods.pipe(...updaters); - matchPipe = (match: string, ...updaters: ParcelUpdater[]): Parcel => this._methods.matchPipe(match, ...updaters); // Advanced methods getInternalLocationShareData = (): * => this._methods.getInternalLocationShareData(); diff --git a/packages/dataparcels/src/parcel/ParcelTypes.js b/packages/dataparcels/src/parcel/ParcelTypes.js index 3068bd34..55747a93 100644 --- a/packages/dataparcels/src/parcel/ParcelTypes.js +++ b/packages/dataparcels/src/parcel/ParcelTypes.js @@ -23,13 +23,4 @@ export default class ParcelTypes { isIndexed: Function = (): boolean => this._isIndexed; isParent: Function = (): boolean => this._isParent; isTopLevel: Function = (): boolean => this._isTopLevel; - - toTypeCode: Function = (): string => { - let c: string = this._isChild ? "C" : "c"; - let e: string = this._isElement ? "E" : "e"; - let i: string = this._isIndexed ? "I" : "i"; - let p: string = this._isParent ? "P" : "p"; - let t: string = this._isTopLevel ? "T" : "t"; - return `${c}${e}${i}${p}${t}`; - }; } diff --git a/packages/dataparcels/src/parcel/__test__/ActionMethods-test.js b/packages/dataparcels/src/parcel/__test__/ActionMethods-test.js index 000c41e7..a1e8d74e 100644 --- a/packages/dataparcels/src/parcel/__test__/ActionMethods-test.js +++ b/packages/dataparcels/src/parcel/__test__/ActionMethods-test.js @@ -25,127 +25,3 @@ test('Parcel.dispatch() should pass handleChange to newly created parcel', () => expect(changeRequest2.nextData.value).toBe(789); expect(changeRequest2.prevData.value).toBe(456); }); - -test('Parcel.batch() should batch actions', () => { - expect.assertions(4); - - var functionCalls = []; - var expectedFunctionCalls = [ - 'batch', - 'onChange(456)', - 'onChange(789)', - 'handleChange' - ]; - - - var data = { - value: 123, - handleChange: (parcel) => { - let {value} = parcel; - expect(value).toBe(789); - functionCalls.push("handleChange"); - } - }; - - new Parcel(data).batch((parcel) => { - functionCalls.push("batch"); - parcel.onChange(456); - expect(parcel.value).toBe(456); - functionCalls.push("onChange(456)"); - parcel.onChange(789); - expect(parcel.value).toBe(789); - functionCalls.push("onChange(789)"); - }); - - expect(expectedFunctionCalls).toEqual(functionCalls); -}); - -test('Parcel.batch() should batch correctly with non-idempotent actions', () => { - expect.assertions(4); - - var functionCalls = []; - var expectedFunctionCalls = [ - 'batch', - 'push(456)', - 'push(789)', - 'handleChange' - ]; - - var data = { - value: [], - handleChange: (parcel) => { - let {value} = parcel; - expect(value).toEqual([456,789]); - functionCalls.push("handleChange"); - } - }; - - new Parcel(data).batch((parcel) => { - functionCalls.push("batch"); - parcel.push(456); - expect(parcel.value).toEqual([456]); - functionCalls.push("push(456)"); - parcel.push(789); - expect(parcel.value).toEqual([456, 789]); - functionCalls.push("push(789)"); - }); - - expect(expectedFunctionCalls).toEqual(functionCalls); -}); - -test('Parcel.batch() should not fire handleChange if no actions called within batch', () => { - var handleChangeCalled = false; - - var data = { - value: 123, - handleChange: (parcel) => { - handleChangeCalled = true; - } - }; - - new Parcel(data).batch((parcel) => {}); - expect(handleChangeCalled).toBe(false); -}); - -test('Parcel.batch() should be nestable', () => { - expect.assertions(5); - - var functionCalls = []; - var expectedFunctionCalls = [ - 'batch', - 'onChange(123)', - 'batch again', - 'onChange(456)', - 'out again', - 'onChange(789)', - 'handleChange' - ]; - - var data = { - value: 123, - handleChange: (parcel) => { - let {value} = parcel; - expect(value).toBe(789); - functionCalls.push("handleChange"); - } - }; - - new Parcel(data).batch((parcel) => { - functionCalls.push("batch"); - parcel.onChange(123); - expect(parcel.value).toBe(123); - functionCalls.push("onChange(123)"); - parcel.batch((parcel) => { - functionCalls.push("batch again"); - parcel.onChange(456); - expect(parcel.value).toBe(456); - functionCalls.push("onChange(456)"); - }); - functionCalls.push("out again"); - parcel.onChange(789); - expect(parcel.value).toBe(789); - functionCalls.push("onChange(789)"); - }); - - expect(expectedFunctionCalls).toEqual(functionCalls); -}); diff --git a/packages/dataparcels/src/parcel/__test__/ElementParcelMethods-test.js b/packages/dataparcels/src/parcel/__test__/ElementParcelMethods-test.js index f8aa95c8..d429299c 100644 --- a/packages/dataparcels/src/parcel/__test__/ElementParcelMethods-test.js +++ b/packages/dataparcels/src/parcel/__test__/ElementParcelMethods-test.js @@ -1,6 +1,8 @@ // @flow import Parcel from '../Parcel'; +import GetAction from '../../util/__test__/GetAction-testUtil'; + test('ElementParcel.insertBefore() should insert', () => { expect.assertions(4); @@ -37,7 +39,7 @@ test('ElementParcel.insertBefore() should insert', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }) .get(1) @@ -47,7 +49,7 @@ test('ElementParcel.insertBefore() should insert', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }) .get("#b") @@ -90,7 +92,7 @@ test('ElementParcel.insertAfter() should insert', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }) .get(1) @@ -100,13 +102,55 @@ test('ElementParcel.insertAfter() should insert', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }) .get("#b") .insertAfter(4); }); +test('ElementParcel.move() should move', () => { + expect.assertions(2); + + var data = { + value: [1,2,3], + child: [ + {key: "#a"}, + {key: "#b"}, + {key: "#c"} + ] + }; + + var expectedData = { + meta: {}, + value: [3,1,2], + key: '^', + child: [ + {key: "#c"}, + {key: "#a"}, + {key: "#b"} + ] + }; + + var expectedAction = { + type: "move", + keyPath: ["#c"], + payload: { + moveKey: 0 + } + }; + + new Parcel({ + ...data, + handleChange: (parcel, changeRequest) => { + expect(expectedData).toEqual(parcel.data); + expect(expectedAction).toEqual(GetAction(changeRequest)); + } + }) + .get(2) + .move(0); +}); + test('ElementParcel.swap() should swap', () => { expect.assertions(2); @@ -142,7 +186,7 @@ test('ElementParcel.swap() should swap', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }) .get(0) @@ -182,7 +226,7 @@ test('ElementParcel.swapNext() should swapNext', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }) .get(0) @@ -198,7 +242,7 @@ test('ElementParcel.swapNext() should swapNext', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }).swapNext("#a"); }); @@ -237,7 +281,7 @@ test('ElementParcel.swapPrev() should swapPrev', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }) .get("#b") diff --git a/packages/dataparcels/src/parcel/__test__/IndexedParcelMethods-test.js b/packages/dataparcels/src/parcel/__test__/IndexedParcelMethods-test.js index e01319bf..1a8818c6 100644 --- a/packages/dataparcels/src/parcel/__test__/IndexedParcelMethods-test.js +++ b/packages/dataparcels/src/parcel/__test__/IndexedParcelMethods-test.js @@ -1,6 +1,8 @@ // @flow import Parcel from '../Parcel'; +import GetAction from '../../util/__test__/GetAction-testUtil'; + test('IndexedParcel.delete() should delete', () => { var data = { @@ -42,9 +44,9 @@ test('IndexedParcel.delete() should delete', () => { }).delete("#a"); expect(indexedHandleChange.mock.calls[0][0].data).toEqual(expectedData); - expect(indexedHandleChange.mock.calls[0][1].actions()[0].toJS()).toEqual(expectedAction); + expect(GetAction(indexedHandleChange.mock.calls[0][1])).toEqual(expectedAction); expect(keyedHandleChange.mock.calls[0][0].data).toEqual(expectedData); - expect(keyedHandleChange.mock.calls[0][1].actions()[0].toJS()).toEqual(expectedAction); + expect(GetAction(keyedHandleChange.mock.calls[0][1])).toEqual(expectedAction); }); @@ -124,9 +126,9 @@ test('IndexedParcel.insertBefore() should insertBefore', () => { }).insertBefore("#b", 4); expect(indexedHandleChange.mock.calls[0][0].data).toEqual(expectedData); - expect(indexedHandleChange.mock.calls[0][1].actions()[0].toJS()).toEqual(expectedAction); + expect(GetAction(indexedHandleChange.mock.calls[0][1])).toEqual(expectedAction); expect(keyedHandleChange.mock.calls[0][0].data).toEqual(expectedData); - expect(keyedHandleChange.mock.calls[0][1].actions()[0].toJS()).toEqual(expectedActionWithKey); + expect(GetAction(keyedHandleChange.mock.calls[0][1])).toEqual(expectedActionWithKey); }); @@ -183,9 +185,49 @@ test('IndexedParcel.insertAfter() should insertAfter', () => { }).insertAfter("#b", 4); expect(indexedHandleChange.mock.calls[0][0].data).toEqual(expectedData); - expect(indexedHandleChange.mock.calls[0][1].actions()[0].toJS()).toEqual(expectedAction); + expect(GetAction(indexedHandleChange.mock.calls[0][1])).toEqual(expectedAction); expect(keyedHandleChange.mock.calls[0][0].data).toEqual(expectedData); - expect(keyedHandleChange.mock.calls[0][1].actions()[0].toJS()).toEqual(expectedActionWithKey); + expect(GetAction(keyedHandleChange.mock.calls[0][1])).toEqual(expectedActionWithKey); +}); + +test('IndexedParcel.move() should move', () => { + expect.assertions(2); + + var data = { + value: [1,2,3], + child: [ + {key: "#a"}, + {key: "#b"}, + {key: "#c"} + ] + }; + + var expectedData = { + meta: {}, + value: [3,1,2], + key: '^', + child: [ + {key: "#c"}, + {key: "#a"}, + {key: "#b"} + ] + }; + + var expectedAction = { + type: "move", + keyPath: [2], + payload: { + moveKey: 0 + } + }; + + new Parcel({ + ...data, + handleChange: (parcel, changeRequest) => { + expect(expectedData).toEqual(parcel.data); + expect(expectedAction).toEqual(GetAction(changeRequest)); + } + }).move(2,0); }); test('IndexedParcel.push() should push', () => { @@ -202,13 +244,14 @@ test('IndexedParcel.push() should push', () => { var expectedData = { meta: {}, - value: [1,2,3,4], + value: [1,2,3,4,5], key: '^', child: [ {key: "#a"}, {key: "#b"}, {key: "#c"}, - {key: "#d"} + {key: "#d"}, + {key: "#e"} ] }; @@ -216,7 +259,7 @@ test('IndexedParcel.push() should push', () => { type: "push", keyPath: [], payload: { - value: 4 + values: [4,5] } }; @@ -224,9 +267,9 @@ test('IndexedParcel.push() should push', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } - }).push(4); + }).push(4,5); }); test('IndexedParcel.pop() should pop', () => { @@ -261,7 +304,7 @@ test('IndexedParcel.pop() should pop', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }).pop(); }); @@ -298,7 +341,7 @@ test('IndexedParcel.shift() should shift', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }).shift(); }); @@ -338,7 +381,7 @@ test('IndexedParcel.swap() should swap', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }).swap(0,2); }); @@ -376,7 +419,7 @@ test('IndexedParcel.swapNext() should swapNext', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }).swapNext(0); @@ -390,7 +433,7 @@ test('IndexedParcel.swapNext() should swapNext', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }).swapNext("#a"); }); @@ -429,7 +472,7 @@ test('IndexedParcel.swapPrev() should swapPrev', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }).swapPrev(1); @@ -443,7 +486,7 @@ test('IndexedParcel.swapPrev() should swapPrev', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }).swapPrev("#b"); }); @@ -462,10 +505,11 @@ test('IndexedParcel.unshift() should unshift', () => { var expectedData = { meta: {}, - value: [4,1,2,3], + value: [4,5,1,2,3], key: '^', child: [ {key: "#d"}, + {key: "#e"}, {key: "#a"}, {key: "#b"}, {key: "#c"} @@ -476,7 +520,7 @@ test('IndexedParcel.unshift() should unshift', () => { type: "unshift", keyPath: [], payload: { - value: 4 + values: [4,5] } }; @@ -484,7 +528,7 @@ test('IndexedParcel.unshift() should unshift', () => { ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } - }).unshift(4); + }).unshift(4,5); }); diff --git a/packages/dataparcels/src/parcel/__test__/ModifyMethods-test.js b/packages/dataparcels/src/parcel/__test__/ModifyMethods-test.js index 52544330..3a0063b6 100644 --- a/packages/dataparcels/src/parcel/__test__/ModifyMethods-test.js +++ b/packages/dataparcels/src/parcel/__test__/ModifyMethods-test.js @@ -1,16 +1,21 @@ // @flow -import Parcel from '../Parcel'; import type ChangeRequest from '../../change/ChangeRequest'; import update from 'unmutable/lib/update'; -test('Parcel.modifyValue() should return a new parcel with updated parcelData', () => { +import Parcel from '../Parcel'; +import ParcelShape from '../../parcelShape/ParcelShape'; +import CancelActionMarker from '../../change/CancelActionMarker'; +import shape from '../../parcelShape/shape'; +import TestValidateValueUpdater from '../../util/__test__/TestValidateValueUpdater-testUtil'; + +test('Parcel.modifyDown() should return a new parcel with updated parcelData', () => { expect.assertions(2); var data = { value: [123] }; var parcel = new Parcel(data).get(0); var updated = parcel - .modifyValue((value: *, parcelData: Parcel) => { + .modifyDown((value: *, parcelData: Parcel) => { expect(parcelData).toBe(parcel); return value + 1; }) @@ -25,216 +30,415 @@ test('Parcel.modifyValue() should return a new parcel with updated parcelData', expect(expectedData).toEqual(updated); }); -test('Parcel.modifyValue() should allow non-parent types to be returned', () => { +test('Parcel.modifyDown() should allow non-parent types to be returned', () => { let updatedValue = new Parcel({ value: 123 }) - .modifyValue(value => value + 1) + .modifyDown(value => value + 1) .value; expect(updatedValue).toEqual(124); }); -test('Parcel.modifyValue() should allow childless parent types to be returned', () => { +test('Parcel.modifyDown() should allow undefined to be returned (unlike modifyUp(parcelShapeUpdater))', () => { let updatedValue = new Parcel({ value: 123 }) - .modifyValue(value => []) + .modifyDown(() => {}) .value; - expect(updatedValue).toEqual([]); + expect(updatedValue).toEqual(undefined); }); -test('Parcel.modifyValue() should allow parent types to be returned if they dont change', () => { - let updatedValue = new Parcel({ - value: [123] - }) - .modifyValue(value => value) - .value; - - expect(updatedValue).toEqual([123]); +test('Parcel.modifyDown() should validate value updater', () => { + TestValidateValueUpdater( + expect, + (value, updater) => new Parcel({value}).modifyDown(updater).value + ); }); -test('Parcel.modifyValue() should recognise if value changes types, and set value if type changes', () => { +test('Parcel.modifyDown() should recognise if value changes types, and set value if type changes', () => { let handleChange = jest.fn(); let parcel = new Parcel({ value: 123, handleChange }) - .modifyValue(value => []) + .modifyDown(value => []) .push(123); expect(handleChange).toHaveBeenCalledTimes(1); expect(handleChange.mock.calls[0][0].value).toEqual([123]); }); -test('Parcel.modifyValue() should have id which is unique to updater', () => { +test('Parcel.modifyDown() should have id which is unique to updater', () => { + let updater = value => []; + let sameA1 = new Parcel().modifyDown(updater); + let sameA2 = new Parcel().modifyDown(updater); + let differentA = new Parcel().modifyDown(a => 1 + 2); + + let sameB1 = new Parcel().modifyDown(shape(updater)); + let sameB2 = new Parcel().modifyDown(shape(updater)); + let differentB = new Parcel().modifyDown(shape(a => 1 + 2)); + + expect(sameA1.id).toBe(sameA2.id); + expect(sameA1.id).not.toBe(differentA.id); + expect(sameB1.id).toBe(sameB2.id); + expect(sameB1.id).not.toBe(differentB.id); +}); + +test('Parcel.modifyUp() should have id which is unique to updater', () => { let updater = value => []; - let parcel = new Parcel().modifyValue(updater); - let parcel2 = new Parcel().modifyValue(updater); - let parcel3 = new Parcel().modifyValue(a => 1 + 2); + let sameA1 = new Parcel().modifyUp(updater); + let sameA2 = new Parcel().modifyUp(updater); + let differentA = new Parcel().modifyUp(a => 1 + 2); + + let sameB1 = new Parcel().modifyUp(shape(updater)); + let sameB2 = new Parcel().modifyUp(shape(updater)); + let differentB = new Parcel().modifyUp(shape(a => 1 + 2)); + + expect(sameA1.id).toBe(sameA2.id); + expect(sameA1.id).not.toBe(differentA.id); + expect(sameB1.id).toBe(sameB2.id); + expect(sameB1.id).not.toBe(differentB.id); +}); - expect(parcel.id).toBe("^.~mv-643198612"); - expect(parcel2.id).toBe("^.~mv-643198612"); // same updater should produce the same hash - expect(parcel3.id).not.toBe("^.~mv-643198612"); // different updater should produce different hash +test('Parcel.modifyUp() should allow you to change the payload of a changed parcel with an updater (and should allow non-parent types to be returned)', () => { + var handleChange = jest.fn(); + new Parcel({ + value: 123, + handleChange + }) + .modifyUp(value => value + 1) + .onChange(456); + + expect(handleChange.mock.calls[0][0].value).toBe(457); +}); + +test('Parcel.modifyUp() should validate value updater', () => { + let handleChange = () => {}; + + TestValidateValueUpdater( + expect, + (value, updater) => new Parcel({ + value: undefined, + handleChange + }) + .modifyUp(updater) + .onChange(value) + ); }); -test('Parcel.modifyChangeBatch() should allow you to change the payload of a changed parcel', () => { - expect.assertions(1); +test('Parcel.modifyUp() should allow changes to meta through', () => { + expect.assertions(2); var data = { value: 123, handleChange: (parcel: Parcel) => { - let {value} = parcel.data; + let {value, meta} = parcel.data; expect(457).toBe(value); + expect({abc: 123}).toEqual(meta); } }; new Parcel(data) - .modifyChangeBatch((parcel: Parcel, changeRequest: ChangeRequest) => { - parcel.set(changeRequest.nextData.value + 1); - }) - .onChange(456); + .modifyUp(value => value + 1) + .update(shape(parcelShape => parcelShape + .set(456) + .setMeta({ + abc: 123 + }) + )); }); -test('Parcel.modifyChangeBatch() should allow you to stop a change by not calling dispatch', () => { - var handleChange = jest.fn(); +test('Parcel.modifyUp() should cancel a change if CancelActionMarker is returned', () => { - var data = { - value: 123, - handleChange - }; + let handleChange = jest.fn(); + let updater = jest.fn(() => CancelActionMarker); - new Parcel(data) - .modifyChangeBatch((parcel: Parcel, changeRequest: ChangeRequest) => { - // nothing here - }) - .onChange(456); + let parcel = new Parcel({ + handleChange, + value: [1,2,3] + }); + + let parcelWithModifier = parcel.modifyUp(updater); + parcelWithModifier.push(4); - expect(handleChange).toHaveBeenCalledTimes(0); + expect(handleChange).not.toHaveBeenCalled(); }); -test('Parcel.modifyChangeBatch() should have id which is unique to updater', () => { - let updater = value => []; - let parcel = new Parcel().modifyChangeBatch(updater); - let parcel2 = new Parcel().modifyChangeBatch(updater); - let parcel3 = new Parcel().modifyChangeBatch(a => "woop"); +test('Parcel.modifyDown(parcelShapeUpdater) should be called with parcelShape and return with no change', () => { + + let handleChange = jest.fn(); + let updater = jest.fn(parcelShape => parcelShape); + + let parcel = new Parcel({ + handleChange, + value: [1,2,3] + }); - expect(parcel.id).toBe("^.~mcb-643198612"); - expect(parcel2.id).toBe("^.~mcb-643198612"); // same updater should produce the same hash - expect(parcel3.id).not.toBe("^.~mcb-643198612"); // different updater should produce different hash + let modifiedParcel = parcel.modifyDown(shape(updater)); + modifiedParcel.push(4); + + expect(modifiedParcel.value).toEqual([1,2,3]); + expect(updater.mock.calls[0][0] instanceof ParcelShape).toBe(true); + expect(updater.mock.calls[0][0].data.value).toEqual([1,2,3]); + expect(handleChange.mock.calls[0][0].data.value).toEqual([1,2,3,4]); }); -test('Parcel.modifyChangeValue() should allow you to change the payload of a changed parcel with an updater (and should allow non-parent types to be returned)', () => { - var handleChange = jest.fn(); - new Parcel({ - value: 123, - handleChange - }) - .modifyChangeValue(value => value + 1) - .onChange(456); +test('Parcel.modifyDown(parcelShapeUpdater) should modify value', () => { + + let handleChange = jest.fn(); + let updater = jest.fn(parcelShape => parcelShape.push(4)); + + let parcel = new Parcel({ + handleChange, + value: [1,2,3] + }); + + let modifiedParcel = parcel.modifyDown(shape(updater)); + modifiedParcel.push(5); + + expect(modifiedParcel.value).toEqual([1,2,3,4]); + expect(handleChange.mock.calls[0][0].data.value).toEqual([1,2,3,4,5]); - expect(handleChange.mock.calls[0][0].value).toBe(457); }); +test('Parcel.modifyDown(parcelShapeUpdater) should work with a returned primitive', () => { -test('Parcel.modifyChangeValue() should allow parent types to be returned', () => { - var handleChange = jest.fn(); - new Parcel({ - value: 123, - handleChange - }) - .modifyChangeValue(value => [123,456]) - .onChange(456); + let handleChange = jest.fn(); + let updater = jest.fn(() => "!!!"); + + let parcel = new Parcel({ + handleChange, + value: [1,2,3] + }); + + let modifiedParcel = parcel.modifyDown(shape(updater)); + modifiedParcel.set(456) - expect(handleChange.mock.calls[0][0].value).toEqual([123,456]); + expect(modifiedParcel.value).toEqual("!!!"); + expect(handleChange.mock.calls[0][0].data.value).toEqual(456); }); -test('Parcel.modifyChangeValue() should allow parent types to be returned if they dont change', () => { - var handleChange = jest.fn(); - new Parcel({ - value: [123], - handleChange - }) - .modifyChangeValue(value => value) - .onChange([456]); +test('Parcel.modifyDown(parcelShapeUpdater) should work with a returned undefined (unlike modifyUp(parcelShapeUpdater))', () => { + + let handleChange = jest.fn(); + let updater = jest.fn(() => {}); + + let parcel = new Parcel({ + handleChange, + value: [1,2,3] + }); + + let modifiedParcel = parcel.modifyDown(shape(updater)); + modifiedParcel.set(456) - expect(handleChange.mock.calls[0][0].value).toEqual([456]); + let expectedValue = undefined; + + expect(modifiedParcel.value).toEqual(expectedValue); + expect(handleChange.mock.calls[0][0].data.value).toEqual(456); }); -test('Parcel.modifyChangeValue() should allow parent types to be returned if they do change', () => { - var handleChange = jest.fn(); - new Parcel({ - value: [123], - handleChange - }) - .modifyChangeValue(update(0, num => num + 1)) - .onChange([456]); +test('Parcel.modifyDown(parcelShapeUpdater) should work with a returned collection containing parcels for children', () => { + + let handleChange = jest.fn(); + let updater = jest.fn(parcelShape => parcelShape.children().reverse()); + + let parcel = new Parcel({ + handleChange, + value: [1,2,3] + }); + + let modifiedParcel = parcel.modifyDown(shape(updater)); + modifiedParcel.push(4); + + let expectedValue = [3,2,1]; - expect(handleChange.mock.calls[0][0].value).toEqual([457]); + expect(modifiedParcel.value).toEqual(expectedValue); + expect(handleChange.mock.calls[0][0].data.value).toEqual([3,2,1,4]); }); -test('Parcel.modifyChangeValue() should allow changes to meta through', () => { - expect.assertions(2); +test('Parcel.modifyUp(parcelShapeUpdater) should be called with parcelShape and return with no change', () => { - var data = { - value: 123, - handleChange: (parcel: Parcel) => { - let {value, meta} = parcel.data; - expect(457).toBe(value); - expect({abc: 123}).toEqual(meta); - } - }; + let handleChange = jest.fn(); + let updater = jest.fn(parcelShape => parcelShape); - new Parcel(data) - .modifyChangeValue(value => value + 1) - .batch(parcel => { - parcel.onChange(456); - parcel.setMeta({ - abc: 123 - }); - }); + let parcel = new Parcel({ + handleChange, + value: 123 + }); + + let parcelWithModifier = parcel.modifyUp(shape(updater)); + let {value} = parcelWithModifier.data; + + expect(value).toEqual(123); + expect(updater).not.toHaveBeenCalled(); + + parcelWithModifier.set(456); + + expect(updater.mock.calls[0][0] instanceof ParcelShape).toBe(true); + expect(updater.mock.calls[0][0].data.value).toEqual(456); + expect(handleChange.mock.calls[0][0].data.value).toEqual(456); +}); + +test('Parcel.modifyUp(parcelShapeUpdater) should modify value', () => { + + let handleChange = jest.fn(); + let updater = jest.fn(parcelShape => parcelShape.push(5)); + + let parcel = new Parcel({ + handleChange, + value: [1,2,3] + }); + + let parcelWithModifier = parcel.modifyUp(shape(updater)); + parcelWithModifier.push(4); + + expect(handleChange.mock.calls[0][0].data.value).toEqual([1,2,3,4,5]); +}); + +test('Parcel.modifyUp(parcelShapeUpdater) should work with a returned primitive', () => { + + let handleChange = jest.fn(); + let updater = jest.fn(() => 123); + + let parcel = new Parcel({ + handleChange, + value: [1,2,3] + }); + + let parcelWithModifier = parcel.modifyUp(shape(updater)); + parcelWithModifier.push(4); + + expect(handleChange.mock.calls[0][0].data.value).toEqual(123); +}); + +test('Parcel.modifyUp(parcelShapeUpdater) should work with a returned collection containing parcels for children', () => { + + let handleChange = jest.fn(); + let updater = jest.fn(parcelShape => parcelShape.children().reverse()); + + let parcel = new Parcel({ + handleChange, + value: [1,2,3] + }); + + let parcelWithModifier = parcel.modifyUp(shape(updater)); + parcelWithModifier.push(4); + + expect(handleChange.mock.calls[0][0].data.value).toEqual([4,3,2,1]); }); +test('Parcel.modifyUp(parcelShapeUpdater) should cancel a change if CancelActionMarker is returned', () => { + + let handleChange = jest.fn(); + let updater = jest.fn(() => CancelActionMarker); + + let parcel = new Parcel({ + handleChange, + value: [1,2,3] + }); + + let parcelWithModifier = parcel.modifyUp(shape(updater)); + parcelWithModifier.push(4); + + expect(handleChange).not.toHaveBeenCalled(); +}); test('Parcel.initialMeta() should work', () => { - expect.assertions(3); + let handleChange = jest.fn(); - let meta = {a:1, b:2}; + let parcel = new Parcel({ + value: 123, + handleChange + }).initialMeta({a:1, b:2}); - var data = { + expect(parcel.data).toEqual({ value: 123, - handleChange: (parcel: Parcel) => { - let {meta} = parcel.data; - expect({a:1, b:3}).toEqual(meta); - expect({a:1, b:3}).toEqual(parcel.initialMeta().meta); - } - }; + meta: { + a: 1, + b: 2 + }, + child: undefined, + key: "^" + }); + + parcel.setMeta({b: 3}); - let parcel = new Parcel(data).initialMeta(meta); - expect(meta).toEqual(parcel.meta); - parcel.setMeta({ - b: 3 + expect(handleChange.mock.calls[0][0].data).toEqual({ + value: 123, + meta: { + a: 1, + b: 3 + }, + child: undefined, + key: "^" }); }); test('Parcel.initialMeta() should merge', () => { - expect.assertions(2); + let handleChange = jest.fn(); - let meta = {a:1, b:2}; - let meta2 = {b:1, c:3}; // this b will be ignored because it will have already been set by the time this is applied + let parcel = new Parcel({ + value: 123, + handleChange + }) + .initialMeta({a:1, b:2}) + .initialMeta({b:3, c:4}) - var data = { + expect(parcel.data).toEqual({ value: 123, - handleChange: (parcel: Parcel) => { - let {meta} = parcel.data; - expect({a:1, b:3, c:3}).toEqual(meta); - } - }; + meta: { + a: 1, + b: 2, + c: 4 + }, + child: undefined, + key: "^" + }); + + parcel.setMeta({d: 5}); - let parcel = new Parcel(data).initialMeta(meta).initialMeta(meta2); - expect({a:1, b:2, c:3}).toEqual(parcel.meta); - parcel.setMeta({ - b: 3 + expect(handleChange.mock.calls[0][0].data).toEqual({ + value: 123, + meta: { + a: 1, + b: 2, + c: 4, + d: 5 + }, + child: undefined, + key: "^" }); }); + +test('Parcel.initialMeta() should do nothing to data if all meta keys are already set', () => { + + let parcel = new Parcel({ + value: 123 + }).initialMeta({a:1, b:2}); + + let parcel2 = parcel.initialMeta({a:1, b:2}); + + expect(parcel2.data).toEqual(parcel.data); +}); + +test('Sanity check: A big strange test of a big strange chain of deep updatery stuff', () => { + + let handleChange = jest.fn(); + let updater = jest.fn(value => value + "333"); + + let parcel = new Parcel({ + handleChange, + value: [1,2,3] + }) + .modifyDown(shape(parcelShape => parcelShape.children().reverse())) // 1. reverse the elements in the parcel (value: [3,2,1]) + .modifyUp(shape(parcelShape => parcelShape.children().reverse())) // 6. reverse the elements in the parcel (value: [3333,2,1]) + .get(0) // 2. get the first element (value: 3) + .modifyDown(value => value + "") // 3. cast number to string value: "3") + .modifyUp(value => parseInt(value, 10)) // 5. cast string to number (value will be: 3333) + .update(updater); // 4. make a change to the data, append three threes (value will be: "3333") + + expect(updater.mock.calls[0][0]).toBe("3"); + expect(handleChange.mock.calls[0][0].value).toEqual([1,2,3333]); +}); diff --git a/packages/dataparcels/src/parcel/__test__/Parcel-test.js b/packages/dataparcels/src/parcel/__test__/Parcel-test.js index cf83e7a3..67e14c04 100644 --- a/packages/dataparcels/src/parcel/__test__/Parcel-test.js +++ b/packages/dataparcels/src/parcel/__test__/Parcel-test.js @@ -24,8 +24,3 @@ test('Parcels should be able to accept just handleChange in config', () => { expect(undefined).toEqual(parcel.value); parcel.onChange(456); }); - -test('Parcel should store debugRender in treeshare', () => { - expect(new Parcel()._treeshare.debugRender).toBe(false); - expect(new Parcel({debugRender: true})._treeshare.debugRender).toBe(true); -}); diff --git a/packages/dataparcels/src/parcel/__test__/ParcelChangeMethods-test.js b/packages/dataparcels/src/parcel/__test__/ParcelChangeMethods-test.js index 031262ae..2de367d0 100644 --- a/packages/dataparcels/src/parcel/__test__/ParcelChangeMethods-test.js +++ b/packages/dataparcels/src/parcel/__test__/ParcelChangeMethods-test.js @@ -1,5 +1,8 @@ // @flow import Parcel from '../Parcel'; +import GetAction from '../../util/__test__/GetAction-testUtil'; +import ParcelShape from '../../parcelShape/ParcelShape'; +import TestValidateValueUpdater from '../../util/__test__/TestValidateValueUpdater-testUtil'; test('Parcel.set() should call the Parcels handleChange function with the new parcelData', () => { expect.assertions(3); @@ -28,7 +31,7 @@ test('Parcel.set() should call the Parcels handleChange function with the new pa handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); expect(expectedData).toEqual(changeRequest.nextData); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }).set(456); }); @@ -68,39 +71,64 @@ test('Parcel.set() should remove and replace child data when setting a deep data }); test('Parcel.update() should call the Parcels handleChange function with the new parcelData', () => { - expect.assertions(3); - var data = { - value: 123 - }; + let handleChange = jest.fn(); + let updater = jest.fn(ii => ii + 1); - var expectedArg = 123; + new Parcel({ + value: 123, + handleChange + }).update(updater); - var expectedData = { - child: undefined, - meta: {}, - value: 456, - key: '^' - }; + expect(updater.mock.calls[0][0]).toBe(123); + expect(handleChange.mock.calls[0][0].data.value).toBe(124); +}); - var expectedAction = { - type: "set", - keyPath: [], - payload: { - value: 456 - } - }; +test('Parcel.update() should validate value updater', () => { + TestValidateValueUpdater( + expect, + (value, updater) => new Parcel({value}).update(updater) + ); +}); + +test('Parcel.update(parcelShape) should call the Parcels handleChange function with the new parcelData', () => { + + let handleChange = jest.fn(); + let updater = jest.fn(parcelShape => parcelShape.push(4)); new Parcel({ - ...data, - handleChange: (parcel, changeRequest) => { - expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); - } - }).update((ii) => { - expect(expectedArg).toEqual(ii); - return 456; - }); + value: [1,2,3], + handleChange + }).update(ParcelShape.update(updater)); + + expect(updater.mock.calls[0][0] instanceof ParcelShape).toBe(true); + expect(handleChange.mock.calls[0][0].data.value).toEqual([1,2,3,4]); +}); + +test('Parcel.update(parcelShape) should work with a returned primitive', () => { + + let handleChange = jest.fn(); + let updater = jest.fn(() => 123); + + new Parcel({ + value: [1,2,3], + handleChange + }).update(ParcelShape.update(updater)); + + expect(handleChange.mock.calls[0][0].data.value).toEqual(123); +}); + +test('Parcel.update(parcelShape) should work with a returned collection containing parcels for children', () => { + + let handleChange = jest.fn(); + let updater = jest.fn(parcelShape => parcelShape.children().reverse()); + + new Parcel({ + value: [1,2,3], + handleChange + }).update(ParcelShape.update(updater)); + + expect(handleChange.mock.calls[0][0].data.value).toEqual([3,2,1]); }); test('Parcel.onChange() should work like set that only accepts a single argument', () => { @@ -129,7 +157,7 @@ test('Parcel.onChange() should work like set that only accepts a single argument ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }).onChange(456); }); @@ -160,7 +188,7 @@ test('Parcel.onChangeDOM() should work like onChange but take the value from eve ...data, handleChange: (parcel, changeRequest) => { expect(expectedData).toEqual(parcel.data); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }).onChangeDOM({ currentTarget: { @@ -204,7 +232,7 @@ test('Parcel.setMeta() should call the Parcels handleChange function with the ne if(changes === 1) { expect(expectedMeta).toEqual(parcel.meta); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); parcel.setMeta({ def: 456 }); @@ -217,96 +245,3 @@ test('Parcel.setMeta() should call the Parcels handleChange function with the ne abc: 123 }); }); - -test('Parcel.updateMeta() should call the Parcels handleChange function with the new meta merged in', () => { - expect.assertions(5); - - var data = { - value: 123 - }; - - var expectedMeta = { - abc: 123 - }; - - var expectedMeta2 = { - abc: 123, - def: 456 - }; - - var expectedAction = { - type: "setMeta", - keyPath: [], - payload: { - meta: { - abc: 123 - } - } - }; - - var changes = 0; - - new Parcel({ - ...data, - handleChange: (parcel, changeRequest) => { - changes++; - - if(changes === 1) { - expect(expectedMeta).toEqual(parcel.meta); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); - parcel.updateMeta(meta => { - expect({abc: 123}).toEqual(meta) - return { - def: 456 - }; - }); - - } else if(changes === 2) { - expect(expectedMeta2).toEqual(parcel.meta); - } - } - }).updateMeta(meta => { - expect({}).toEqual(meta) - return { - abc: 123 - }; - }); -}); - -test('Parcel.setChangeRequestMeta() should set change request meta', () => { - var data = { - value: 123, - handleChange: (parcel, changeRequest) => { - expect({a: 3, b: 2}).toEqual(changeRequest.changeRequestMeta); - } - }; - - var parcel = new Parcel(data).batch(parcel => { - parcel.setChangeRequestMeta({a: 1}); - parcel.onChange(456); - parcel.setChangeRequestMeta({b: 2}); - parcel.setChangeRequestMeta({a: 3}); - }); -}); - -test('Parcel.ping() should call the Parcels handleChange function with no change', () => { - expect.assertions(1); - - var data = { - value: 123 - }; - - var expectedData = { - child: undefined, - meta: {}, - value: 123, - key: '^' - }; - - new Parcel({ - ...data, - handleChange: (parcel, changeRequest) => { - expect(expectedData).toEqual(parcel.data); - } - }).ping(456); -}); diff --git a/packages/dataparcels/src/parcel/__test__/ParcelGetMethods-test.js b/packages/dataparcels/src/parcel/__test__/ParcelGetMethods-test.js index 008c8e60..5d1e2c39 100644 --- a/packages/dataparcels/src/parcel/__test__/ParcelGetMethods-test.js +++ b/packages/dataparcels/src/parcel/__test__/ParcelGetMethods-test.js @@ -78,28 +78,6 @@ test('Parcel.spreadDOM(notFoundValue) returns an object with notFoundValue', () expect(parcel3.spreadDOM("???").value).toBe("123"); }); -test('Parcel.hasDispatched() should say if a parcel has dispatched from the current parcels path location', () => { - expect.assertions(6); - - let p = new Parcel({ - value: { - abc: 123, - def: 456 - }, - handleChange: (p2) => { - expect(p2.hasDispatched()).toBe(true); - expect(p2.get('abc').hasDispatched()).toBe(true); - expect(p2.get('def').hasDispatched()).toBe(false); - } - }); - - expect(p.hasDispatched()).toBe(false); - expect(p.get('abc').hasDispatched()).toBe(false); - expect(p.get('def').hasDispatched()).toBe(false); - - p.get('abc').onChange(789); -}); - test('Parcel.setInternalLocationShareData() and Parcel.getInternalLocationShareData should store data per location', () => { let p = new Parcel({ @@ -176,71 +154,3 @@ test('Parcel.pipe() should pass itself in and return what pipe() returns', () => expect(updater2.mock.calls[0][0]).toBe(parcel1); expect(result).toBe(parcel2); }); - -test('Parcel.matchPipe() should match self', () => { - let updater = jest.fn(_ => _); - - let parcel = new Parcel({value: 123}).matchPipe(".", updater); - - expect(updater.mock.calls.length).toBe(1); - expect(updater.mock.calls[0][0].id).toBe("^.~mp"); -}); - -test('Parcel.matchPipe() should match child', () => { - let updater = jest.fn(_ => _); - - let parcel = new Parcel({value: [123]}).matchPipe(".#a", updater).get(0); - - expect(updater.mock.calls.length).toBe(1); - expect(updater.mock.calls[0][0].id).toBe("^.~mp.#a"); -}); - -test('Parcel.matchPipe() should match child with a deep origin', () => { - let updater = jest.fn(_ => _); - - let parcel = new Parcel({ - value: { - abc: { - def: 123 - } - } - }) - .get("abc") - .matchPipe(".def", updater) - .get("def"); - - expect(updater.mock.calls.length).toBe(1); - expect(updater.mock.calls[0][0].id).toBe("^.abc.~mp.def"); -}); - -test('Parcel.matchPipe() can match child', () => { - let updater = jest.fn(_ => _); - - let parcel = new Parcel({ - value: { - abc: 123, - def: 456 - } - }).matchPipe(".abc", updater); - - parcel.get("abc"); - parcel.get("def"); - - expect(updater.mock.calls.length).toBe(1); - expect(updater.mock.calls[0][0].id).toBe("^.~mp.abc"); -}); - - -test('When matching only self, Parcel.matchPipe() should pass a cloned version of itself in and return what matchPipe() returns', () => { - var parcel1 = new Parcel(); - var parcel2 = new Parcel(); - - let updater1 = jest.fn(_ => _); - let updater2 = jest.fn(_ => parcel2); - - var result = parcel1.matchPipe(".", updater1, updater2); - - expect(updater1.mock.calls[0][0].value).toBe(parcel1.value); // parcel will be a cloned version of itself - expect(updater2.mock.calls[0][0]).toBe(updater1.mock.calls[0][0]); // second updater should receive same parcel as first - expect(result).toBe(parcel2); -}); diff --git a/packages/dataparcels/src/parcel/__test__/ParcelGetters-test.js b/packages/dataparcels/src/parcel/__test__/ParcelGetters-test.js index 3bd4e412..722fb058 100644 --- a/packages/dataparcels/src/parcel/__test__/ParcelGetters-test.js +++ b/packages/dataparcels/src/parcel/__test__/ParcelGetters-test.js @@ -73,12 +73,12 @@ test('Parcel.id should return the Parcels id', () => { ['something.:@']: 123 } }; - expect("^").toBe(new Parcel(data).id); - expect("^.a").toBe(new Parcel(data).get("a").id); - expect("^.~mv-364997092.a").toBe(new Parcel(data).modifyValue(ii => ii).get("a").id); - expect("^.a.#a").toBe(new Parcel(data).getIn(["a",0]).id); - expect("^.something%.%:%@").toBe(new Parcel(data).get("something.:@").id); - expect("^.b").toBe(new Parcel(data).get("b").id); + expect(new Parcel(data).id).toBe("^"); + expect(new Parcel(data).get("a").id).toBe("^.a"); + expect(new Parcel(data).modifyDown(ii => ii).get("a").id).toBe("^.~md-364997092.a"); + expect(new Parcel(data).getIn(["a",0]).id).toBe("^.a.#a"); + expect(new Parcel(data).get("something.:@").id).toBe("^.something%.%:%@"); + expect(new Parcel(data).get("b").id).toBe("^.b"); // t.is("#a", new Parcel(data).getIn(["a",?????]).id); TODO }); @@ -89,28 +89,42 @@ test('Parcel.path should return the Parcels path', () => { ['something.:@']: 123 } }; - expect([]).toEqual(new Parcel(data).path); - expect(["a"]).toEqual(new Parcel(data).get("a").path); - expect(["a"]).toEqual(new Parcel(data).modifyValue(ii => ii).get("a").path); - expect(["a","#a"]).toEqual(new Parcel(data).getIn(["a",0]).path); - expect(["something.:@"]).toEqual(new Parcel(data).get("something.:@").path); - expect(["b"]).toEqual(new Parcel(data).get("b").path); + expect(new Parcel(data).path).toEqual([]); + expect(new Parcel(data).get("a").path).toEqual(["a"]); + expect(new Parcel(data).modifyDown(ii => ii).get("a").path).toEqual(["a"]); + expect(new Parcel(data).getIn(["a",0]).path).toEqual(["a","#a"]); + expect(new Parcel(data).get("something.:@").path).toEqual(["something.:@"]); + expect(new Parcel(data).get("b").path).toEqual(["b"]); // t.is("#a", new Parcel(data).getIn(["a",?????]).path); TODO }); -test('Parcel._id.typedPathString() should return the Parcels typed path', () => { - var data = { - value: { - a: [1,2,3], - ['something.:@']: 123 - } - }; - expect("^:ceiPT").toEqual(new Parcel(data)._id.typedPathString()); - expect("^:ceiPT").toEqual(new Parcel(data).modifyValue(ii => ii)._id.typedPathString()); - expect("^:ceiPT.a:CeIPt").toEqual(new Parcel(data).get("a")._id.typedPathString()); - expect("^:ceiPT.a:CeIPt").toEqual(new Parcel(data).modifyValue(ii => ii).get("a")._id.typedPathString()); - expect("^:ceiPT.a:CeIPt.#a:CEipt").toEqual(new Parcel(data).getIn(["a",0])._id.typedPathString()); - expect("^:ceiPT.something%.%:%@:Ceipt").toEqual(new Parcel(data).get("something.:@")._id.typedPathString()); - expect("^:ceiPT.b:Ceipt").toEqual(new Parcel(data).get("b")._id.typedPathString()); - // t.is("#a", new Parcel(data).getIn(["a",?????])._id.typedPathString()); TODO + +test('Parcel should throw errors when attempted to set getters', () => { + let readOnly = 'This property is read-only'; + + let parcel = new Parcel(); + + expect(() => { + parcel.data = 123; + }).toThrow(readOnly); + + expect(() => { + parcel.value = 123; + }).toThrow(readOnly); + + expect(() => { + parcel.meta = 123; + }).toThrow(readOnly); + + expect(() => { + parcel.key = 123; + }).toThrow(readOnly); + + expect(() => { + parcel.id = 123; + }).toThrow(readOnly); + + expect(() => { + parcel.path = 123; + }).toThrow(readOnly); }); diff --git a/packages/dataparcels/src/parcel/__test__/ParcelTypes-test.js b/packages/dataparcels/src/parcel/__test__/ParcelTypes-test.js index 835a5c06..411532d5 100644 --- a/packages/dataparcels/src/parcel/__test__/ParcelTypes-test.js +++ b/packages/dataparcels/src/parcel/__test__/ParcelTypes-test.js @@ -11,7 +11,6 @@ test('ParcelTypes should correctly identify primitive values', () => { expect(new Parcel(data).isChild()).toBe(false); expect(new Parcel(data).isElement()).toBe(false); expect(new Parcel(data).isTopLevel()).toBe(true); - expect(new Parcel(data)._parcelTypes.toTypeCode()).toBe("ceipT"); }); test('ParcelTypes should correctly identify date', () => { @@ -23,7 +22,6 @@ test('ParcelTypes should correctly identify date', () => { expect(new Parcel(data).isChild()).toBe(false); expect(new Parcel(data).isElement()).toBe(false); expect(new Parcel(data).isTopLevel()).toBe(true); - expect(new Parcel(data)._parcelTypes.toTypeCode()).toBe("ceipT"); }); test('ParcelTypes should correctly identify object values', () => { @@ -37,7 +35,6 @@ test('ParcelTypes should correctly identify object values', () => { expect(new Parcel(data).isChild()).toBe(false); expect(new Parcel(data).isElement()).toBe(false); expect(new Parcel(data).isTopLevel()).toBe(true); - expect(new Parcel(data)._parcelTypes.toTypeCode()).toBe("ceiPT"); }); test('ParcelTypes should correctly identify class instance values', () => { @@ -52,7 +49,6 @@ test('ParcelTypes should correctly identify class instance values', () => { expect(new Parcel(data).isChild()).toBe(false); expect(new Parcel(data).isElement()).toBe(false); expect(new Parcel(data).isTopLevel()).toBe(true); - expect(new Parcel(data)._parcelTypes.toTypeCode()).toBe("ceipT"); }); test('ParcelTypes should correctly identify unmutable compatible class instance values', () => { @@ -68,7 +64,6 @@ test('ParcelTypes should correctly identify unmutable compatible class instance expect(new Parcel(data).isChild()).toBe(false); expect(new Parcel(data).isElement()).toBe(false); expect(new Parcel(data).isTopLevel()).toBe(true); - expect(new Parcel(data)._parcelTypes.toTypeCode()).toBe("ceiPT"); }); @@ -83,7 +78,6 @@ test('ParcelTypes should correctly identify Immutable.js Map values', () => { expect(new Parcel(data).isChild()).toBe(false); expect(new Parcel(data).isElement()).toBe(false); expect(new Parcel(data).isTopLevel()).toBe(true); - expect(new Parcel(data)._parcelTypes.toTypeCode()).toBe("ceiPT"); }); @@ -96,7 +90,6 @@ test('ParcelTypes should correctly identify array values', () => { expect(new Parcel(data).isChild()).toBe(false); expect(new Parcel(data).isElement()).toBe(false); expect(new Parcel(data).isTopLevel()).toBe(true); - expect(new Parcel(data)._parcelTypes.toTypeCode()).toBe("ceIPT"); }); test('ParcelTypes should correctly identify Immutable.js List values', () => { @@ -108,7 +101,6 @@ test('ParcelTypes should correctly identify Immutable.js List values', () => { expect(new Parcel(data).isChild()).toBe(false); expect(new Parcel(data).isElement()).toBe(false); expect(new Parcel(data).isTopLevel()).toBe(true); - expect(new Parcel(data)._parcelTypes.toTypeCode()).toBe("ceIPT"); }); test('ParcelTypes should correctly identify child values', () => { @@ -122,7 +114,6 @@ test('ParcelTypes should correctly identify child values', () => { expect(new Parcel(data).get("a").isChild()).toBe(true); expect(new Parcel(data).get("a").isElement()).toBe(false); expect(new Parcel(data).get("a").isTopLevel()).toBe(false); - expect(new Parcel(data).get("a")._parcelTypes.toTypeCode()).toBe("Ceipt"); }); test('ParcelTypes should correctly identify element values', () => { @@ -134,14 +125,13 @@ test('ParcelTypes should correctly identify element values', () => { expect(new Parcel(data).get(0).isChild()).toBe(true); expect(new Parcel(data).get(0).isElement()).toBe(true); expect(new Parcel(data).get(0).isTopLevel()).toBe(false); - expect(new Parcel(data).get(0)._parcelTypes.toTypeCode()).toBe("CEipt"); }); test('ParcelTypes should correctly identify top level values after modifiers', () => { var data = { value: [1,2,3] }; - expect(new Parcel(data).modifyValue(ii => ii).isTopLevel()).toBe(true); + expect(new Parcel(data).modifyDown(ii => ii).isTopLevel()).toBe(true); }); // method creators diff --git a/packages/dataparcels/src/parcel/__test__/ParentChangeMethods-test.js b/packages/dataparcels/src/parcel/__test__/ParentChangeMethods-test.js index 890da415..b4ecf8da 100644 --- a/packages/dataparcels/src/parcel/__test__/ParentChangeMethods-test.js +++ b/packages/dataparcels/src/parcel/__test__/ParentChangeMethods-test.js @@ -1,6 +1,7 @@ // @flow import Parcel from '../Parcel'; import map from 'unmutable/lib/map'; +import ParcelShape from '../../parcelShape/ParcelShape'; test('ParentParcel.set(key) should call the Parcels handleChange function with the new parcelData', () => { expect.assertions(1); @@ -19,21 +20,36 @@ test('ParentParcel.set(key) should call the Parcels handleChange function with t }); test('ParentParcel.update(key) should call the Parcels handleChange function with the new parcelData', () => { - expect.assertions(2); + let updater = jest.fn(ii => ii + 1); + let handleChange = jest.fn(); - var data = { + new Parcel({ value: { - a: "!!!" + abc: 123 }, - handleChange: (parcel) => { - let {value} = parcel.data; - expect({a: "???"}).toEqual(value); - } - }; + handleChange + }).update("abc", updater); - new Parcel(data).update("a", ii => { - expect("!!!").toBe(ii); - return "???"; + expect(updater.mock.calls[0][0]).toBe(123); + expect(handleChange.mock.calls[0][0].data.value).toEqual({ + abc: 124 + }); +}); + +test('ParentParcel.update(key, parcelShapeUpdater) should call the Parcels handleChange function with the new parcelData', () => { + let updater = jest.fn(parcelShape => parcelShape.push(4)); + let handleChange = jest.fn(); + + new Parcel({ + value: { + abc: [1,2,3] + }, + handleChange + }).update("abc", ParcelShape.update(updater)); + + expect(updater.mock.calls[0][0] instanceof ParcelShape).toBe(true); + expect(handleChange.mock.calls[0][0].data.value).toEqual({ + abc: [1,2,3,4] }); }); @@ -94,6 +110,27 @@ test('ParentParcel.updateIn(keyPath) should call the Parcels handleChange functi }); }); +test('ParentParcel.updateIn(keyPath, parcelShapeUpdater) should call the Parcels handleChange function with the new parcelData', () => { + let updater = jest.fn(parcelShape => parcelShape.push(4)); + let handleChange = jest.fn(); + + new Parcel({ + value: { + abc: { + def: [1,2,3] + } + }, + handleChange + }).updateIn(["abc", "def"], ParcelShape.update(updater)); + + expect(updater.mock.calls[0][0] instanceof ParcelShape).toBe(true); + expect(handleChange.mock.calls[0][0].data.value).toEqual({ + abc: { + def: [1,2,3,4] + } + }); +}); + test('ParentParcel.deleteIn(keyPath) should delete deeply', () => { var data = { value: { diff --git a/packages/dataparcels/src/parcel/__test__/ParentGetMethods-test.js b/packages/dataparcels/src/parcel/__test__/ParentGetMethods-test.js index a3d9db88..6df48db9 100644 --- a/packages/dataparcels/src/parcel/__test__/ParentGetMethods-test.js +++ b/packages/dataparcels/src/parcel/__test__/ParentGetMethods-test.js @@ -1,6 +1,9 @@ // @flow import Parcel from '../Parcel'; import TestTimeExecution from '../../util/__test__/TestTimeExecution-testUtil'; +import GetAction from '../../util/__test__/GetAction-testUtil'; + +import every from 'unmutable/lib/every'; import map from 'unmutable/lib/map'; import range from 'unmutable/lib/util/range'; @@ -37,7 +40,7 @@ test('ParentParcel.get(key) should return a new child Parcel', () => { b: 4 }, handleChange: (parcel, changeRequest) => { - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); expect(expectedValue).toEqual(parcel.value); } }; @@ -125,7 +128,7 @@ test('ParentParcel.get(key).get(key) should return a new child Parcel and chain }, handleChange: (parcel, changeRequest) => { expect(parcel.value).toEqual(expectedValue); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }; @@ -214,7 +217,7 @@ test('ParentParcel.getIn(keyPath) should return a new descendant Parcel', () => }, handleChange: (parcel, changeRequest) => { expect(parcel.value).toEqual(expectedValue); - expect(expectedAction).toEqual(changeRequest.actions()[0].toJS()); + expect(expectedAction).toEqual(GetAction(changeRequest)); } }; @@ -261,6 +264,37 @@ test('ParentParcel.getIn(keyPath) should cope with non existent keypaths', () => expect(descendantParcel2.value).toEqual("!!!"); }); +test('ParentParcel.children() should get child parcels in original collection', () => { + let children = new Parcel({ + value: {a:1,b:2,c:3} + }).children(); + + // test if child parcels are made + expect(every(parcel => parcel instanceof Parcel)(children)).toBe(true); + + // test if child parcel contents are good + expect(map(parcel => parcel.value)(children)).toEqual({ + a: 1, + b: 2, + c: 3 + }); +}); + +test('ParentParcel.children() should get child parcels in original collection with a mapper', () => { + let mapper = jest.fn(parcel => parcel.value); + let children = new Parcel({ + value: {a:1,b:2,c:3} + }).children(mapper); + + // test if child parcel contents are good + expect(children).toEqual({ + a: 1, + b: 2, + c: 3 + }); +}); + + test('ParentParcel.toObject() should make an object', () => { var data = { value: {a:1,b:2,c:3}, @@ -274,9 +308,25 @@ test('ParentParcel.toObject() should make an object', () => { var obj = map(ii => ii.value)(parcel.toObject()); expect(expectedObject).toEqual(obj); +}); + +test('ParentParcel.toObject() should make an object from an array', () => { + var data = { + value: [1,2,3], + meta: { + a: {a:4,b:5,c:6} + } + }; + + var expectedObject = {"0":1,"1":2,"2":3}; + var parcel = new Parcel(data); + var obj = map(ii => ii.value)(parcel.toObject()); + + expect(obj).toEqual(expectedObject); }); + test('ParentParcel.toObject() should make an object with a mapper', () => { var data = { value: {a:1,b:2,c:3}, diff --git a/packages/dataparcels/src/parcel/__test__/TopLevelMethods-test.js b/packages/dataparcels/src/parcel/__test__/TopLevelMethods-test.js deleted file mode 100644 index bea9bb21..00000000 --- a/packages/dataparcels/src/parcel/__test__/TopLevelMethods-test.js +++ /dev/null @@ -1,22 +0,0 @@ -// @flow -import Parcel from '../Parcel'; - -test('Parcel.batchAndReturn() should batch actions and return parcel', () => { - let handleChange = jest.fn(); - let newParcel = new Parcel({ - value: { - abc: 123, - def: 456 - }, - handleChange - }).batchAndReturn((parcel) => { - parcel.get('abc').onChange(789); - }); - - expect(newParcel.value).toEqual({ - abc: 789, - def: 456 - }); - - expect(handleChange).toHaveBeenCalledTimes(0); -}); diff --git a/packages/dataparcels/src/parcel/methods/ActionMethods.js b/packages/dataparcels/src/parcel/methods/ActionMethods.js index 412708a1..92a88460 100644 --- a/packages/dataparcels/src/parcel/methods/ActionMethods.js +++ b/packages/dataparcels/src/parcel/methods/ActionMethods.js @@ -1,14 +1,11 @@ // @flow import type Action from '../../change/Action'; import type Parcel from '../Parcel'; -import type {ParcelData} from '../../types/Types'; -import type {ParcelBatcher} from '../../types/Types'; import Types from '../../types/Types'; import ChangeRequest from '../../change/ChangeRequest'; export default (_this: Parcel): Object => ({ - dispatch: (dispatchable: Action|Action[]|ChangeRequest) => { Types(`dispatch()`, `dispatchable`, `dispatchable`)(dispatchable); @@ -17,8 +14,6 @@ export default (_this: Parcel): Object => ({ _onHandleChange } = _this; - _this._treeshare.dispatch.markPathAsDispatched(_this.path); - let changeRequest: ChangeRequest = dispatchable instanceof ChangeRequest ? dispatchable : new ChangeRequest(dispatchable); @@ -33,48 +28,23 @@ export default (_this: Parcel): Object => ({ changeRequest.toConsole(); } - if(_this._dispatchBuffer) { - _this._dispatchBuffer(changeRequest); - return; - } - if(_onHandleChange) { let changeRequestWithBase = changeRequest._setBaseParcel(_this); + let parcelData = changeRequestWithBase.nextData; + + if(!parcelData) { + return; + } + let parcelWithChangedData = _this._create({ handleChange: _onHandleChange, - parcelData: changeRequestWithBase.nextData + parcelData, + lastOriginId: changeRequest.originId }); _onHandleChange(parcelWithChangedData, changeRequestWithBase); return; } _onDispatch && _onDispatch(changeRequest); - }, - - batch: (batcher: ParcelBatcher, changeRequest: ?ChangeRequest) => { - Types(`batch()`, `batcher`, `function`)(batcher); - - let parcelData: ParcelData = _this._parcelData; - let lastBuffer = _this._dispatchBuffer; - - let buffer = changeRequest - ? changeRequest.updateActions(() => []) - : new ChangeRequest(); - - _this._dispatchBuffer = (changeRequest: ChangeRequest) => { - buffer = buffer.merge(changeRequest); - _this._parcelData = changeRequest - ._setBaseParcel(_this) - .nextData; - }; - - batcher(_this); - _this._dispatchBuffer = lastBuffer; - if(buffer.actions().length === 0) { - return; - } - - _this._parcelData = parcelData; - _this.dispatch(buffer); } }); diff --git a/packages/dataparcels/src/parcel/methods/ElementChangeMethods.js b/packages/dataparcels/src/parcel/methods/ElementChangeMethods.js index 972bb2c8..f1cfafd3 100644 --- a/packages/dataparcels/src/parcel/methods/ElementChangeMethods.js +++ b/packages/dataparcels/src/parcel/methods/ElementChangeMethods.js @@ -16,6 +16,11 @@ export default (_this: Parcel, dispatch: Function): Object => ({ dispatch(ActionCreators.insertBeforeSelf(value)); }, + moveSelf: (key: Key|Index) => { + Types(`moveSelf()`, `key`, `keyIndex`)(key); + dispatch(ActionCreators.moveSelf(key)); + }, + swapNextSelf: () => { dispatch(ActionCreators.swapNextSelf()); }, diff --git a/packages/dataparcels/src/parcel/methods/IndexedChangeMethods.js b/packages/dataparcels/src/parcel/methods/IndexedChangeMethods.js index e4e411d3..56552d11 100644 --- a/packages/dataparcels/src/parcel/methods/IndexedChangeMethods.js +++ b/packages/dataparcels/src/parcel/methods/IndexedChangeMethods.js @@ -18,8 +18,14 @@ export default (_this: Parcel, dispatch: Function): Object => ({ dispatch(ActionCreators.insertBefore(key, value)); }, - push: (value: *) => { - dispatch(ActionCreators.push(value)); + move: (keyA: Key|Index, keyB: Key|Index) => { + Types(`move()`, `keyA`, `keyIndex`)(keyA); + Types(`move()`, `keyB`, `keyIndex`)(keyB); + dispatch(ActionCreators.move(keyA, keyB)); + }, + + push: (...values: Array<*>) => { + dispatch(ActionCreators.push(values)); }, pop: () => { @@ -46,7 +52,7 @@ export default (_this: Parcel, dispatch: Function): Object => ({ dispatch(ActionCreators.swapPrev(key)); }, - unshift: (value: *) => { - dispatch(ActionCreators.unshift(value)); + unshift: (...values: Array<*>) => { + dispatch(ActionCreators.unshift(values)); } }); diff --git a/packages/dataparcels/src/parcel/methods/ModifyMethods.js b/packages/dataparcels/src/parcel/methods/ModifyMethods.js index 77f34ff6..4cc429cc 100644 --- a/packages/dataparcels/src/parcel/methods/ModifyMethods.js +++ b/packages/dataparcels/src/parcel/methods/ModifyMethods.js @@ -1,19 +1,22 @@ // @flow import type ChangeRequest from '../../change/ChangeRequest'; import type Parcel from '../Parcel'; +import type {ParcelData} from '../../types/Types'; +import type {ParcelDataEvaluator} from '../../types/Types'; import type {ParcelMeta} from '../../types/Types'; +import type {ParcelValueUpdater} from '../../types/Types'; +import type {ParcelShapeUpdateFunction} from '../../types/Types'; + +import {checkCancellation} from '../../change/CancelActionMarker'; import Types from '../../types/Types'; +import setSelf from '../../parcelData/setSelf'; +import setMetaDefault from '../../parcelData/setMetaDefault'; +import ValidateValueUpdater from '../../util/ValidateValueUpdater'; -import ParcelTypes from '../ParcelTypes'; import HashString from '../../util/HashString'; -import equals from 'unmutable/lib/equals'; import filterNot from 'unmutable/lib/filterNot'; import has from 'unmutable/lib/has'; -import isEmpty from 'unmutable/lib/isEmpty'; -import merge from 'unmutable/lib/merge'; -import set from 'unmutable/lib/set'; -import setIn from 'unmutable/lib/setIn'; import pipe from 'unmutable/lib/util/pipe'; import pipeWith from 'unmutable/lib/util/pipeWith'; @@ -21,113 +24,79 @@ let HashFunction = (fn: Function): string => `${HashString(fn.toString())}`; export default (_this: Parcel): Object => ({ - modifyValue: (updater: Function): Parcel => { - Types(`modifyValue()`, `updater`, `function`)(updater); - - let {value} = _this._parcelData; - let updatedValue = updater(value, _this); - let updatedType = new ParcelTypes(updatedValue); + _pushModifierId: (prefix: string, updater: Function): string => { + let id = updater._isParcelUpdater + ? `s${HashFunction(updater._updater)}` + : HashFunction(updater); - if(process.env.NODE_ENV !== 'production' && updatedType.isParent() && !isEmpty()(updatedValue) && !equals(value)(updatedValue)) { - console.warn(`modifyValue(): please ensure you do not change the shape of the value, as changing the data shape or moving children within the data shape can cause dataparcels to misplace its keying and meta information.`); /* eslint-disable-line */ - } - - let updatedTypeChanged: boolean = updatedType.isParent() !== _this._parcelTypes.isParent() - || updatedType.isIndexed() !== _this._parcelTypes.isIndexed(); + return _this._id.pushModifier(`${prefix}-${id}`); + }, - let onDispatch; - if(updatedTypeChanged) { - onDispatch = (changeRequest: ChangeRequest) => { - _this.batch( - (parcel: Parcel) => { - parcel.set(updatedValue); - parcel.dispatch(changeRequest); - }, - changeRequest - ); + _getModifierUpdater: (updater: ParcelValueUpdater|ParcelShapeUpdateFunction): ParcelDataEvaluator => { + // $FlowFixMe - flow just cant make the connection between updater._isParcelUpdater and the choice between ParcelValueUpdater or ParcelShapeUpdateFunction + return updater._isParcelUpdater + // $FlowFixMe - this branch should only be hit with ParcelShapeUpdateFunction + ? (parcelData: ParcelData): ParcelData => updater(parcelData) + : (parcelData: ParcelData): ParcelData => { + let {value} = parcelData; + let updatedValue = updater(value, _this); + ValidateValueUpdater(value, updatedValue); + return setSelf(updatedValue)(parcelData); }; - } + }, + _boundarySplit: ({handleChange}: *): Parcel => { return _this._create({ - id: _this._id.pushModifier(`mv-${HashFunction(updater)}`), - parcelData: { - ..._this._parcelData, - value: updatedValue - }, - onDispatch + id: _this._id.pushModifier('bs'), + parent: _this._parent, + handleChange, + treeshare: _this._treeshare.boundarySplit() }); }, - modifyChangeBatch: (batcher: Function): Parcel => { - Types(`modifyChangeBatch()`, `batcher`, `function`)(batcher); + modifyDown: (updater: ParcelValueUpdater|ParcelShapeUpdateFunction): Parcel => { + Types(`modifyDown()`, `updater`, `function`)(updater); + let parcelDataUpdater: ParcelDataEvaluator = _this._methods._getModifierUpdater(updater); return _this._create({ - id: _this._id.pushModifier(`mcb-${HashFunction(batcher)}`), + id: _this._methods._pushModifierId('md', updater), + parcelData: parcelDataUpdater(_this._parcelData), onDispatch: (changeRequest: ChangeRequest) => { - _this.batch( - (parcel: Parcel) => batcher(parcel, changeRequest._setBaseParcel(parcel)), - changeRequest - ); + _this.dispatch(changeRequest._addPre(parcelDataUpdater)); } }); }, - modifyChangeValue: (updater: Function): Parcel => { - Types(`modifyChangeValue()`, `updater`, `function`)(updater); - return _this.modifyChangeBatch((parcel: Parcel, changeRequest: ChangeRequest) => { - - let {value} = changeRequest.nextData; - let type = new ParcelTypes(value); - - let updatedValue = updater(value, _this); + modifyUp: (updater: ParcelValueUpdater|ParcelShapeUpdateFunction): Parcel => { + Types(`modifyUp()`, `updater`, `function`)(updater); + let parcelDataUpdater: ParcelDataEvaluator = pipe( + _this._methods._getModifierUpdater(updater), + checkCancellation + ); - if(type.isParent() && process.env.NODE_ENV !== 'production' && !equals(value)(updatedValue)) { - console.warn(`modifyChangeValue(): please ensure you do not change the shape of the value, as changing the data shape or moving children within the data shape can cause dataparcels to misplace its keying and meta information.`); /* eslint-disable-line */ + return _this._create({ + id: _this._methods._pushModifierId('mu', updater), + onDispatch: (changeRequest: ChangeRequest) => { + _this.dispatch(changeRequest._addPost(parcelDataUpdater)); } - - // dispatch all non-value actions in this change request - let valueActionFilter = actions => actions.filter(action => !action.isValueAction()); - parcel.dispatch(changeRequest.updateActions(valueActionFilter)); - - parcel.set(updatedValue); }); }, - initialMeta: (initialMeta: ParcelMeta = {}): Parcel => { + initialMeta: (initialMeta: ParcelMeta): Parcel => { Types(`initialMeta()`, `initialMeta`, `object`)(initialMeta); let {meta} = _this._parcelData; - let partialMetaToSet = pipeWith( + let parcelDataUpdater = pipeWith( initialMeta, - filterNot((value, key) => has(key)(meta)) + filterNot((value, key) => has(key)(meta)), + setMetaDefault ); - let metaSetter = isEmpty()(partialMetaToSet) - ? ii => ii - : pipe( - setIn(['parcelData', 'meta'], merge(partialMetaToSet)(meta)), - set('onDispatch', (changeRequest: ChangeRequest) => { - _this.batch((parcel: Parcel) => { - parcel.setMeta(partialMetaToSet); - parcel.dispatch(changeRequest); - }); - }) - ); - - return pipeWith( - { - id: _this._id.pushModifier('im') - }, - metaSetter, - _this._create - ); - }, - - _boundarySplit: ({handleChange}: *): Parcel => { return _this._create({ - id: _this._id.pushModifier('bs'), - parent: _this._parent, - handleChange, - treeshare: _this._treeshare.boundarySplit() + id: _this._id.pushModifier('im'), + parcelData: parcelDataUpdater(_this._parcelData), + onDispatch: (changeRequest: ChangeRequest) => { + _this.dispatch(changeRequest._addPost(parcelDataUpdater)); + } }); } }); diff --git a/packages/dataparcels/src/parcel/methods/ParcelChangeMethods.js b/packages/dataparcels/src/parcel/methods/ParcelChangeMethods.js index da4dad1f..38a1392b 100644 --- a/packages/dataparcels/src/parcel/methods/ParcelChangeMethods.js +++ b/packages/dataparcels/src/parcel/methods/ParcelChangeMethods.js @@ -1,14 +1,12 @@ // @flow import type Parcel from '../Parcel'; import type {ParcelMeta} from '../../types/Types'; -import type {ParcelMetaUpdater} from '../../types/Types'; import type {ParcelValueUpdater} from '../../types/Types'; +import type {ParcelShapeUpdateFunction} from '../../types/Types'; import Types from '../../types/Types'; -import ChangeRequest from '../../change/ChangeRequest'; import ActionCreators from '../../change/ActionCreators'; - -import pipeWith from 'unmutable/lib/util/pipeWith'; +import ValidateValueUpdater from '../../util/ValidateValueUpdater'; export default (_this: Parcel, dispatch: Function) => ({ @@ -16,14 +14,22 @@ export default (_this: Parcel, dispatch: Function) => ({ dispatch(ActionCreators.setSelf(value)); }, - updateSelf: (updater: ParcelValueUpdater) => { + updateSelf: (updater: ParcelValueUpdater|ParcelShapeUpdateFunction) => { Types(`updateSelf()`, `updater`, `function`)(updater); - _this.set(updater(_this.value)); + if(updater._isParcelUpdater) { + // $FlowFixMe - this branch should only be hit with ParcelShapeUpdateFunction + let updated = updater(_this._parcelData); + dispatch(ActionCreators.setData(updated)); + return; + } + + let {value} = _this; + let updatedValue = updater(value, _this); + ValidateValueUpdater(value, updatedValue); + _this.set(updatedValue); }, - onChange: (value: *) => { - _this.set(value); - }, + onChange: _this.set, onChangeDOM: (event: Object) => { Types(`onChangeDOM()`, `event`, `event`)(event); @@ -33,25 +39,5 @@ export default (_this: Parcel, dispatch: Function) => ({ setMeta: (partialMeta: ParcelMeta) => { Types(`setMeta()`, `partialMeta`, `object`)(partialMeta); dispatch(ActionCreators.setMeta(partialMeta)); - }, - - updateMeta: (updater: ParcelMetaUpdater) => { - Types(`updateMeta()`, `updater`, `function`)(updater); - let {meta} = _this._parcelData; - pipeWith( - meta, - updater, - Types(`updateMeta()`, `the result of updater()`, `object`), - _this.setMeta - ); - }, - - setChangeRequestMeta: (partialMeta: ParcelMeta) => { - Types(`setChangeRequestMeta()`, `partialMeta`, `object`)(partialMeta); - dispatch(new ChangeRequest().setChangeRequestMeta(partialMeta)); - }, - - ping: () => { - dispatch(ActionCreators.ping()); } }); diff --git a/packages/dataparcels/src/parcel/methods/ParcelGetMethods.js b/packages/dataparcels/src/parcel/methods/ParcelGetMethods.js index fd2b117b..49df2bdd 100644 --- a/packages/dataparcels/src/parcel/methods/ParcelGetMethods.js +++ b/packages/dataparcels/src/parcel/methods/ParcelGetMethods.js @@ -2,10 +2,8 @@ import type ChangeRequest from '../../change/ChangeRequest'; import type Parcel from '../../parcel/Parcel'; import type {ParcelUpdater} from '../../types/Types'; -import type {MatchPipe} from '../../types/Types'; import Types from '../../types/Types'; -import Matcher from '../../match/Matcher'; import DeletedParcelMarker from '../../parcelData/DeletedParcelMarker'; import map from 'unmutable/lib/map'; @@ -49,51 +47,6 @@ export default (_this: Parcel) => ({ ); }, - matchPipe: (match: string, ...updaters: ParcelUpdater[]): Parcel => { - Types(`matchPipe()`, `first param`, `string`)(match); - updaters.forEach(Types(`matchPipe()`, `all updaters`, `function`)); - - let parcel = _this._create({ - id: _this._id.pushModifier('mp'), - matchPipes: [ - ..._this._matchPipes, - { - depth: _this.path.length, - match, - updater: pipe( - ...pipeWith( - updaters, - map(updater => pipe( - updater, - Types(`matchPipe() `, `the result of all functions`, `parcel`) - )) - ) - ) - } - ] - }); - return parcel._methods._applyMatchPipes(); - }, - - _applyMatchPipes: (): Parcel => { - let typedPathString = _this._id.typedPathString(); - return pipeWith( - _this, - ..._this._matchPipes - .filter(({match, depth}: MatchPipe): boolean => { - let matched = Matcher(typedPathString, match, depth); - return matched; - }) - .map(({updater}: MatchPipe) => updater) - ); - }, - - // Status methods - - hasDispatched: (): boolean => { - return _this._treeshare.dispatch.hasPathDispatched(_this.path); - }, - // Side-effect methods log: (name: string): Parcel => { diff --git a/packages/dataparcels/src/parcel/methods/ParentChangeMethods.js b/packages/dataparcels/src/parcel/methods/ParentChangeMethods.js index e729fd8c..0cf2276b 100644 --- a/packages/dataparcels/src/parcel/methods/ParentChangeMethods.js +++ b/packages/dataparcels/src/parcel/methods/ParentChangeMethods.js @@ -3,6 +3,8 @@ import type {Index} from '../../types/Types'; import type {Key} from '../../types/Types'; import type Parcel from '../Parcel'; import type {ParcelValueUpdater} from '../../types/Types'; +import type {ParcelShapeUpdateFunction} from '../../types/Types'; + import Types from '../../types/Types'; export default (_this: Parcel /*, dispatch: Function*/): Object => ({ @@ -11,10 +13,9 @@ export default (_this: Parcel /*, dispatch: Function*/): Object => ({ _this.get(key).set(value); }, - update: (key: Key|Index, updater: ParcelValueUpdater) => { - Types(`update()`, `key`, `keyIndex`)(key); - Types(`update()`, `updater`, `function`)(updater); - _this.get(key).update(updater); + setIn: (keyPath: Array, value: *) => { + Types(`setIn()`, `keyPath`, `keyIndexPath`)(keyPath); + _this.getIn(keyPath).set(value); }, delete: (key: Key|Index) => { @@ -22,19 +23,20 @@ export default (_this: Parcel /*, dispatch: Function*/): Object => ({ _this.get(key).delete(); }, - setIn: (keyPath: Array, value: *) => { - Types(`setIn()`, `keyPath`, `keyIndexPath`)(keyPath); - _this.getIn(keyPath).set(value); + deleteIn: (keyPath: Array) => { + Types(`deleteIn()`, `keyPath`, `keyIndexPath`)(keyPath); + _this.getIn(keyPath).delete(); }, - updateIn: (keyPath: Array, updater: ParcelValueUpdater) => { - Types(`updateIn()`, `keyPath`, `keyIndexPath`)(keyPath); + update: (key: Key|Index, updater: ParcelValueUpdater|ParcelShapeUpdateFunction) => { + Types(`update()`, `key`, `keyIndex`)(key); Types(`update()`, `updater`, `function`)(updater); - _this.getIn(keyPath).update(updater); + _this.get(key).update(updater); }, - deleteIn: (keyPath: Array) => { - Types(`deleteIn()`, `keyPath`, `keyIndexPath`)(keyPath); - _this.getIn(keyPath).delete(); + updateIn: (keyPath: Array, updater: ParcelValueUpdater|ParcelShapeUpdateFunction) => { + Types(`updateIn()`, `keyPath`, `keyIndexPath`)(keyPath); + Types(`updateIn()`, `updater`, `function`)(updater); + _this.getIn(keyPath).update(updater); } }); diff --git a/packages/dataparcels/src/parcel/methods/ParentGetMethods.js b/packages/dataparcels/src/parcel/methods/ParentGetMethods.js index 47df6ad6..682c562a 100644 --- a/packages/dataparcels/src/parcel/methods/ParentGetMethods.js +++ b/packages/dataparcels/src/parcel/methods/ParentGetMethods.js @@ -4,15 +4,18 @@ import type {Index} from '../../types/Types'; import type {Key} from '../../types/Types'; import type Parcel from '../Parcel'; import type {ParcelMapper} from '../../types/Types'; +import type {ParentType} from '../../types/Types'; import Types from '../../types/Types'; import parcelGet from '../../parcelData/get'; import parcelHas from '../../parcelData/has'; import prepareChildKeys from '../../parcelData/prepareChildKeys'; +import clone from 'unmutable/lib/clone'; import map from 'unmutable/lib/map'; import size from 'unmutable/lib/size'; import toArray from 'unmutable/lib/toArray'; +import toObject from 'unmutable/lib/toObject'; import pipeWith from 'unmutable/lib/util/pipeWith'; export default (_this: Parcel) => ({ @@ -68,21 +71,24 @@ export default (_this: Parcel) => ({ return parcel; }, - toObject: (mapper: ParcelMapper): { [key: string]: * } => { - Types(`toObject()`, `mapper`, `function`)(mapper); + children: (mapper: ParcelMapper): ParentType => { + Types(`children()`, `mapper`, `function`)(mapper); return pipeWith( _this._parcelData.value, - map((ii: *, key: string|number): * => { - let item = _this.get(key); - return mapper(item, key, _this); - }) + clone(), + map((value, key) => mapper(_this.get(key), key, _this)) ); }, - toArray: (mapper: ParcelMapper): Array<*> => { + toObject: (mapper: ParcelMapper): { [key: string]: Parcel } => { + Types(`toObject()`, `mapper`, `function`)(mapper); + return toObject()(_this.children(mapper)); + }, + + toArray: (mapper: ParcelMapper): Array => { Types(`toArray()`, `mapper`, `function`)(mapper); - return toArray()(_this.toObject(mapper)); + return toArray()(_this.children(mapper)); }, size: (): number => size()(_this.value) diff --git a/packages/dataparcels/src/parcel/methods/TopLevelMethods.js b/packages/dataparcels/src/parcel/methods/TopLevelMethods.js deleted file mode 100644 index 0f00ec2c..00000000 --- a/packages/dataparcels/src/parcel/methods/TopLevelMethods.js +++ /dev/null @@ -1,26 +0,0 @@ -// @flow -import type Parcel from '../Parcel'; -import type {ParcelBatcher} from '../../types/Types'; -import Types from '../../types/Types'; - -import ChangeRequest from '../../change/ChangeRequest'; - -export default (_this: Parcel): Object => ({ - - batchAndReturn: (batcher: ParcelBatcher, changeRequest: ?ChangeRequest): ?Parcel => { - Types(`batchAndReturn()`, `batcher`, `function`)(batcher); - - let newParcel; - - // swap out the parcels real _onHandleChange with a spy - let handleChange = _this._onHandleChange; - _this._onHandleChange = (parcel: Parcel) => { - newParcel = parcel; - newParcel._onHandleChange = handleChange; - }; - _this.batch(batcher, changeRequest); - _this._onHandleChange = handleChange; - - return newParcel; - } -}); diff --git a/packages/dataparcels/src/parcelData/__test__/getIn-test.js b/packages/dataparcels/src/parcelData/__test__/getIn-test.js new file mode 100644 index 00000000..14677525 --- /dev/null +++ b/packages/dataparcels/src/parcelData/__test__/getIn-test.js @@ -0,0 +1,221 @@ +// @flow +import getIn from '../getIn'; + +test('getIn should work with objects', () => { + let parcelData = { + value: { + a: { + b: 1 + } + } + }; + let expectedParcelData = { + value: { + b: 1 + }, + key: "a" + }; + + expect(expectedParcelData).toEqual(getIn(['a'])(parcelData)); +}); + +test('getIn should work with objects with child data', () => { + let parcelData = { + value: { + a: { + b: 1 + } + }, + meta: { + abc: 123 + }, + child: { + a: { + key: "a", + meta: { + def: 456 + }, + child: { + b: { + key: "b", + meta: { + ghi: 789 + } + } + } + } + } + }; + + let expectedParcelData = { + value: { + b: 1 + }, + meta: { + def: 456 + }, + key: "a", + child: { + b: { + key: "b", + meta: { + ghi: 789 + } + } + } + }; + + expect(expectedParcelData).toEqual(getIn(['a'])(parcelData)); +}); + +test('getIn should work with objects with hashkey - just to make sure that hashkeys are only converted to properties when used on an indexed parcel', () => { + let parcelData = { + value: { + ["#a"]: { + b: 1 + } + } + }; + let expectedParcelData = { + value: { + b: 1 + }, + key: "#a" + }; + + expect(expectedParcelData).toEqual(getIn(['#a'])(parcelData)); +}); + + +test('getIn should not clone value', () => { + let parcelData = { + value: { + a: { + b: 1 + } + } + }; + expect(parcelData.value.a).toBe(getIn(['a'])(parcelData).value); +}); + +test('getIn should work with arrays that dont have keys yet', () => { + let parcelData = { + value: ['abc'] + }; + + let expectedParcelData = { + value: 'abc', + key: "#a" + }; + + expect(getIn([0])(parcelData)).toEqual(expectedParcelData); +}); + +test('getIn should work with arrays that dont have keys yet with hashkey', () => { + let parcelData = { + value: ['abc', 'def'] + }; + + let expectedParcelData = { + value: 'def', + key: "#b" + }; + + expect(getIn(["#b"])(parcelData)).toEqual(expectedParcelData); +}); + +test('getIn should work with objects that already have children, and not recreate children, even if incorrect', () => { + let parcelData = { + value: { + a: { + b: 1 + } + }, + child: { + a: { + key: 'AsdasdsdDS' + } + } + }; + let expectedParcelData = { + value: { + b: 1 + }, + key: 'AsdasdsdDS' + }; + + expect(expectedParcelData).toEqual(getIn(['a'])(parcelData)); +}); + +test('getIn should work with non existent keys', () => { + let parcelData = { + value: { + a: { + b: 1 + } + } + }; + let expectedParcelData = { + value: undefined, + key: "z" + }; + + expect(expectedParcelData).toEqual(getIn(['z'])(parcelData)); + + let expectedParcelData2 = { + value: "!!!", + key: "z" + }; + + expect(expectedParcelData2).toEqual(getIn(['z'], "!!!")(parcelData)); +}); + +// +// deep tests +// + +test('getIn should work deeply', () => { + let parcelData = { + value: { + a: { + b: 1 + } + } + }; + let expectedParcelData = { + value: 1, + key: "b" + }; + + expect(getIn(['a', 'b'])(parcelData)).toEqual(expectedParcelData); +}); + +test('getIn should return undefined if asking through a non-parent', () => { + let parcelData = { + value: { + a: 123 + } + }; + let expectedParcelData = { + value: undefined, + key: "b" + }; + + expect(getIn(['a', 'b'])(parcelData)).toEqual(expectedParcelData); +}); + +test('getIn should return undefined if asking for a non-existent key', () => { + let parcelData = { + value: { + a: { + b: 123 + } + } + }; + let expectedParcelData = { + value: undefined, + key: "c" + }; + + expect(getIn(['a', 'c'])(parcelData)).toEqual(expectedParcelData); +}); diff --git a/packages/dataparcels/src/parcelData/__test__/update-test.js b/packages/dataparcels/src/parcelData/__test__/update-test.js new file mode 100644 index 00000000..81676a5f --- /dev/null +++ b/packages/dataparcels/src/parcelData/__test__/update-test.js @@ -0,0 +1,73 @@ +// @flow +import update from '../update'; + +const addThree = (parcelData) => ({ + ...parcelData, + value: parcelData.value + 3 +}); + +test('update should work', () => { + let parcelData = { + value: {a:1,b:2,c:3}, + child: { + a: {key:"a", meta: {abc: 123}}, + b: {key:"b"}, + c: {key:"c"} + } + }; + + let expectedParcelData = { + value: {a:15,b:2,c:3}, + child: { + a: {key:"a", meta: {abc: 123}}, + b: {key:"b"}, + c: {key:"c"} + } + }; + + let expectedExisting = { + value: 1, + meta: {abc: 123}, + key: "a" + }; + + let updated = update('a', (existing) => { + expect(existing).toEqual(expectedExisting); + return { + ...existing, + value: existing.value + 14 + }; + })(parcelData); + + expect(updated).toEqual(expectedParcelData); +}); + +test('update should work with unkeyed arrays', () => { + let parcelData = { + value: [1,2,3] + }; + + let expectedParcelData = { + value: [1,2,6], + child: [ + {key: "#a"}, + {key: "#b"}, + {key: "#c"} + ] + }; + + let expectedExisting = { + value: 3, + key: "#c" + }; + + let updated = update('#c', (existing) => { + expect(existing).toEqual(expectedExisting); + return { + ...existing, + value: 6 + }; + })(parcelData); + + expect(updated).toEqual(expectedParcelData); +}); diff --git a/packages/dataparcels/src/parcelData/__test__/updateChildKeys-test.js b/packages/dataparcels/src/parcelData/__test__/updateChildKeys-test.js index f29a48b8..5ee23a58 100644 --- a/packages/dataparcels/src/parcelData/__test__/updateChildKeys-test.js +++ b/packages/dataparcels/src/parcelData/__test__/updateChildKeys-test.js @@ -165,3 +165,11 @@ test('updateChildKeys() retains deep keys on arrays', () => { expect(expectedData).toEqual(updateChildKeys()(data)); }); + +test('updateChildKeys() should passthrough data that doesnt require keys / data that doesnt have child', () => { + let data = { + value: 123 + }; + + expect(data).toEqual(updateChildKeys()(data)); +}); diff --git a/packages/dataparcels/src/parcelData/getIn.js b/packages/dataparcels/src/parcelData/getIn.js new file mode 100644 index 00000000..a56d051e --- /dev/null +++ b/packages/dataparcels/src/parcelData/getIn.js @@ -0,0 +1,22 @@ +// @flow +import type {Key} from '../types/Types'; +import type {Index} from '../types/Types'; +import type {ParcelData} from '../types/Types'; + +import get from './get'; +import has from './has'; +import keyOrIndexToKey from './keyOrIndexToKey'; +import isParentValue from './isParentValue'; + +export default (keyPath: Array, notFoundValue: ?*) => (parcelData: ParcelData): ParcelData => { + for(let key of keyPath) { + if(!isParentValue(parcelData.value) || !has(key)(parcelData)) { + return { + value: notFoundValue, + key: keyOrIndexToKey(key)(parcelData) + }; + } + parcelData = get(key, notFoundValue)(parcelData); + } + return parcelData; +}; diff --git a/packages/dataparcels/src/parcelData/insert.js b/packages/dataparcels/src/parcelData/insert.js index e0d2a900..bc0ac18b 100644 --- a/packages/dataparcels/src/parcelData/insert.js +++ b/packages/dataparcels/src/parcelData/insert.js @@ -11,7 +11,7 @@ import updateChildKeys from './updateChildKeys'; import insert from 'unmutable/lib/insert'; import size from 'unmutable/lib/size'; -export default (key: Key|Index, newValue: *, after: boolean = false) => (parcelData: ParcelData): ParcelData => { +export default (after: boolean) => (key: Key|Index, newValue: *) => (parcelData: ParcelData): ParcelData => { let parcelDataWithChildKeys = prepareChildKeys()(parcelData); let index: ?Index = keyOrIndexToIndex(key)(parcelDataWithChildKeys); diff --git a/packages/dataparcels/src/parcelData/insertAfter.js b/packages/dataparcels/src/parcelData/insertAfter.js new file mode 100644 index 00000000..82323cbd --- /dev/null +++ b/packages/dataparcels/src/parcelData/insertAfter.js @@ -0,0 +1,3 @@ +// @flow +import insert from './insert'; +export default insert(true); diff --git a/packages/dataparcels/src/parcelData/insertBefore.js b/packages/dataparcels/src/parcelData/insertBefore.js new file mode 100644 index 00000000..50f14de3 --- /dev/null +++ b/packages/dataparcels/src/parcelData/insertBefore.js @@ -0,0 +1,3 @@ +// @flow +import insert from './insert'; +export default insert(false); diff --git a/packages/dataparcels/src/parcelData/move.js b/packages/dataparcels/src/parcelData/move.js new file mode 100644 index 00000000..a448c0e4 --- /dev/null +++ b/packages/dataparcels/src/parcelData/move.js @@ -0,0 +1,3 @@ +// @flow +import swapOrMove from './swapOrMove'; +export default swapOrMove('move'); diff --git a/packages/dataparcels/src/parcelData/push.js b/packages/dataparcels/src/parcelData/push.js index 32afade6..523eda23 100644 --- a/packages/dataparcels/src/parcelData/push.js +++ b/packages/dataparcels/src/parcelData/push.js @@ -5,11 +5,13 @@ import updateChildKeys from './updateChildKeys'; import push from 'unmutable/lib/push'; -export default (newValue: *) => (parcelData: ParcelData): ParcelData => { +export default (...newValues: Array<*>) => (parcelData: ParcelData): ParcelData => { let {value, child, ...rest} = prepareChildKeys()(parcelData); + let emptyChildren = newValues.map(() => ({})); + return updateChildKeys()({ ...rest, - value: push(newValue)(value), - child: push({})(child) + value: push(...newValues)(value), + child: push(...emptyChildren)(child) }); }; diff --git a/packages/dataparcels/src/parcelData/setMetaDefault.js b/packages/dataparcels/src/parcelData/setMetaDefault.js new file mode 100644 index 00000000..03038953 --- /dev/null +++ b/packages/dataparcels/src/parcelData/setMetaDefault.js @@ -0,0 +1,10 @@ +// @flow +import type {ParcelData} from '../types/Types'; + +export default (newMeta: *) => ({meta = {}, ...rest}: ParcelData): ParcelData => ({ /* eslint-disable-line no-unused-vars */ + ...rest, + meta: { + ...newMeta, + ...meta + } +}); diff --git a/packages/dataparcels/src/parcelData/setSelf.js b/packages/dataparcels/src/parcelData/setSelf.js index 2ad47a27..c3771f19 100644 --- a/packages/dataparcels/src/parcelData/setSelf.js +++ b/packages/dataparcels/src/parcelData/setSelf.js @@ -1,28 +1,21 @@ // @flow -import type {ParcelData} from '../types/Types'; +import type {ParcelDataEvaluator} from '../types/Types'; import isParentValue from './isParentValue'; import updateChild from './updateChild'; import updateChildKeys from './updateChildKeys'; import del from 'unmutable/lib/delete'; -import pipeWith from 'unmutable/lib/util/pipeWith'; +import set from 'unmutable/lib/set'; +import pipeIf from 'unmutable/lib/util/pipeIf'; +import pipe from 'unmutable/lib/util/pipe'; -export default (value: *, keepChild: boolean) => (parcelData: ParcelData): ParcelData => { - let result = { - ...parcelData, - value - }; - - result = keepChild ? result : del('child')(result); - - if(!isParentValue(value)) { - return result; - } - - return pipeWith( - result, +export default (value: *): ParcelDataEvaluator => pipe( + set('value', value), + del('child'), + pipeIf( + () => isParentValue(value), updateChild(), updateChildKeys() - ); -}; + ) +); diff --git a/packages/dataparcels/src/parcelData/swap.js b/packages/dataparcels/src/parcelData/swap.js index 9f4123a9..cac3c883 100644 --- a/packages/dataparcels/src/parcelData/swap.js +++ b/packages/dataparcels/src/parcelData/swap.js @@ -1,29 +1,3 @@ // @flow -import type {Index} from '../types/Types'; -import type {Key} from '../types/Types'; -import type {ParcelData} from '../types/Types'; - -import prepareChildKeys from './prepareChildKeys'; -import keyOrIndexToIndex from './keyOrIndexToIndex'; - -import swap from 'unmutable/lib/swap'; - -export default (keyA: Key|Index, keyB: Key|Index) => (parcelData: ParcelData): ParcelData => { - - let parcelDataWithChildKeys = prepareChildKeys()(parcelData); - - let indexA: ?Index = keyOrIndexToIndex(keyA)(parcelDataWithChildKeys); - let indexB: ?Index = keyOrIndexToIndex(keyB)(parcelDataWithChildKeys); - - if(typeof indexA === "undefined" || typeof indexB === "undefined") { - return parcelData; - } - - let {value, child} = parcelDataWithChildKeys; - - return { - ...parcelDataWithChildKeys, - value: swap(indexA, indexB)(value), - child: swap(indexA, indexB)(child) - }; -}; +import swapOrMove from './swapOrMove'; +export default swapOrMove('swap'); diff --git a/packages/dataparcels/src/parcelData/swapNext.js b/packages/dataparcels/src/parcelData/swapNext.js new file mode 100644 index 00000000..973b4f75 --- /dev/null +++ b/packages/dataparcels/src/parcelData/swapNext.js @@ -0,0 +1,3 @@ +// @flow +import swapNextPrev from './swapNextPrev'; +export default swapNextPrev(true); diff --git a/packages/dataparcels/src/parcelData/swapNextPrev.js b/packages/dataparcels/src/parcelData/swapNextPrev.js index bd47329d..226883b3 100644 --- a/packages/dataparcels/src/parcelData/swapNextPrev.js +++ b/packages/dataparcels/src/parcelData/swapNextPrev.js @@ -10,7 +10,7 @@ import wrapNumber from './wrapNumber'; import size from 'unmutable/lib/size'; import swap from 'unmutable/lib/swap'; -export default (key: Key|Index, next: boolean = false) => (parcelData: ParcelData): ParcelData => { +export default (next: boolean) => (key: Key|Index) => (parcelData: ParcelData): ParcelData => { let parcelDataWithChildKeys = prepareChildKeys()(parcelData); let indexA: ?Index = keyOrIndexToIndex(key)(parcelDataWithChildKeys); diff --git a/packages/dataparcels/src/parcelData/swapOrMove.js b/packages/dataparcels/src/parcelData/swapOrMove.js new file mode 100644 index 00000000..f15e048c --- /dev/null +++ b/packages/dataparcels/src/parcelData/swapOrMove.js @@ -0,0 +1,33 @@ +// @flow +import type {Index} from '../types/Types'; +import type {Key} from '../types/Types'; +import type {ParcelData} from '../types/Types'; + +import prepareChildKeys from './prepareChildKeys'; +import keyOrIndexToIndex from './keyOrIndexToIndex'; + +import move from 'unmutable/lib/move'; +import swap from 'unmutable/lib/swap'; + +export default (swapOrMove: string): Function => { + let fn = swapOrMove === 'swap' ? swap : move; + return (keyA: Key|Index, keyB: Key|Index) => (parcelData: ParcelData): ParcelData => { + + let parcelDataWithChildKeys = prepareChildKeys()(parcelData); + + let indexA: ?Index = keyOrIndexToIndex(keyA)(parcelDataWithChildKeys); + let indexB: ?Index = keyOrIndexToIndex(keyB)(parcelDataWithChildKeys); + + if(typeof indexA === "undefined" || typeof indexB === "undefined") { + return parcelData; + } + + let {value, child} = parcelDataWithChildKeys; + + return { + ...parcelDataWithChildKeys, + value: fn(indexA, indexB)(value), + child: fn(indexA, indexB)(child) + }; + }; +}; diff --git a/packages/dataparcels/src/parcelData/swapPrev.js b/packages/dataparcels/src/parcelData/swapPrev.js new file mode 100644 index 00000000..0d86bd4e --- /dev/null +++ b/packages/dataparcels/src/parcelData/swapPrev.js @@ -0,0 +1,3 @@ +// @flow +import swapNextPrev from './swapNextPrev'; +export default swapNextPrev(false); diff --git a/packages/dataparcels/src/parcelData/unshift.js b/packages/dataparcels/src/parcelData/unshift.js index d687af79..43920a33 100644 --- a/packages/dataparcels/src/parcelData/unshift.js +++ b/packages/dataparcels/src/parcelData/unshift.js @@ -5,11 +5,13 @@ import updateChildKeys from './updateChildKeys'; import unshift from 'unmutable/lib/unshift'; -export default (newValue: *) => (parcelData: ParcelData): ParcelData => { +export default (...newValues: Array<*>) => (parcelData: ParcelData): ParcelData => { let {value, child, ...rest} = prepareChildKeys()(parcelData); + let emptyChildren = newValues.map(() => ({})); + return updateChildKeys()({ ...rest, - value: unshift(newValue)(value), - child: unshift({})(child) + value: unshift(...newValues)(value), + child: unshift(...emptyChildren)(child) }); }; diff --git a/packages/dataparcels/src/parcelData/update.js b/packages/dataparcels/src/parcelData/update.js new file mode 100644 index 00000000..a81d731e --- /dev/null +++ b/packages/dataparcels/src/parcelData/update.js @@ -0,0 +1,67 @@ +// @flow +import type {Index} from '../types/Types'; +import type {Key} from '../types/Types'; +import type {Property} from '../types/Types'; +import type {ParcelData} from '../types/Types'; + +import get from './get'; +import isParentValue from './isParentValue'; +import prepareChildKeys from './prepareChildKeys'; +import keyOrIndexToProperty from './keyOrIndexToProperty'; +import updateChild from './updateChild'; +import updateChildKeys from './updateChildKeys'; + +import set from 'unmutable/lib/set'; +import update from 'unmutable/lib/update'; +import pipeWith from 'unmutable/lib/util/pipeWith'; + +export default (key: Key|Index, updater: Function) => (parcelData: ParcelData): ParcelData => { + let parcelDataWithChildKeys = prepareChildKeys()(parcelData); + let property: ?Property = keyOrIndexToProperty(key)(parcelDataWithChildKeys); + + if(typeof property === "undefined") { + return parcelDataWithChildKeys; + } + + let updatedData = pipeWith( + parcelDataWithChildKeys, + get(key), + updater + ); + + let { + value: updatedValue, + child: updatedChild, + ...updatedChildValues + } = updatedData; + + let value = pipeWith( + parcelDataWithChildKeys.value, + set(property, updatedValue) + ); + + let child = pipeWith( + parcelDataWithChildKeys.child, + update(property, (node) => ({ + ...node, + child: updatedChild, + ...updatedChildValues + })) + ); + + let result = { + ...parcelDataWithChildKeys, + value, + child + }; + + if(isParentValue(result.value)) { + return pipeWith( + result, + updateChild(), + updateChildKeys() + ); + } + + return result; +}; diff --git a/packages/dataparcels/src/parcelData/updateChildKeys.js b/packages/dataparcels/src/parcelData/updateChildKeys.js index 49f721b5..8dc607ff 100644 --- a/packages/dataparcels/src/parcelData/updateChildKeys.js +++ b/packages/dataparcels/src/parcelData/updateChildKeys.js @@ -31,6 +31,10 @@ function toIntKey(str: ?string): ?number { export default () => (parcelData: ParcelData): ParcelData => { let {value, child} = parcelData; + if(!child) { + return parcelData; + } + if(!isIndexed(value)) { let updateChild = map((node, key) => set('key', key)(node)); diff --git a/packages/dataparcels/src/parcelData/updateIn.js b/packages/dataparcels/src/parcelData/updateIn.js index 542f5229..eecea8ed 100644 --- a/packages/dataparcels/src/parcelData/updateIn.js +++ b/packages/dataparcels/src/parcelData/updateIn.js @@ -1,73 +1,17 @@ // @flow import type {Index} from '../types/Types'; import type {Key} from '../types/Types'; -import type {Property} from '../types/Types'; -import type {ParcelData} from '../types/Types'; +import type {ParcelDataEvaluator} from '../types/Types'; -import get from './get'; -import isParentValue from './isParentValue'; -import prepareChildKeys from './prepareChildKeys'; -import keyOrIndexToProperty from './keyOrIndexToProperty'; -import updateChild from './updateChild'; -import updateChildKeys from './updateChildKeys'; +import update from './update'; +import composeWith from 'unmutable/lib/composeWith'; -import set from 'unmutable/lib/set'; -import update from 'unmutable/lib/update'; -import pipeWith from 'unmutable/lib/util/pipeWith'; - -let updateIn = (keyPath: Array, updater: Function) => (parcelData: ParcelData): ParcelData => { +export default (keyPath: Array, updater: Function): ParcelDataEvaluator => { if(keyPath.length === 0) { - return updater(parcelData); - } - - let [key, ...rest] = keyPath; - let parcelDataWithChildKeys = prepareChildKeys()(parcelData); - let property: ?Property = keyOrIndexToProperty(key)(parcelDataWithChildKeys); - - if(typeof property === "undefined") { - return parcelDataWithChildKeys; + return updater; } - - let before = pipeWith( - parcelDataWithChildKeys, - get(key) - ); - - let { - value: updatedValue, - child: updatedChild, - ...updatedChildValues - } = updateIn(rest, updater)(before); - - let value = pipeWith( - parcelDataWithChildKeys.value, - set(property, updatedValue) + return composeWith( + ...keyPath.map((key: Key|Index) => (next) => update(key, next)), + updater ); - - let child = pipeWith( - parcelDataWithChildKeys.child, - update(property, (node) => ({ - ...node, - child: updatedChild, - ...updatedChildValues - })) - ); - - let result = { - ...parcelDataWithChildKeys, - value, - child - }; - - if(isParentValue(result.value)) { - return pipeWith( - result, - updateChild(), - updateChildKeys() - ); - } - - return result; }; - -export default updateIn; diff --git a/packages/dataparcels/src/parcelId/ParcelId.js b/packages/dataparcels/src/parcelId/ParcelId.js index 3d2dbe69..89fe2998 100644 --- a/packages/dataparcels/src/parcelId/ParcelId.js +++ b/packages/dataparcels/src/parcelId/ParcelId.js @@ -13,19 +13,16 @@ export const stringifyPath = (path: string[]): string => path.map(escapeKey).joi const DEFAULT_PARCELID_DATA = { id: ["^"], - path: ["^"], - typedPath: ["^"] + path: ["^"] }; export default class ParcelId { _id: string[]; _path: string[]; - _typedPath: string[]; constructor(parcelIdData: ParcelIdData = DEFAULT_PARCELID_DATA) { this._id = parcelIdData.id; this._path = parcelIdData.path; - this._typedPath = parcelIdData.typedPath; } _create: Function = (data: Object): ParcelId => { @@ -44,10 +41,6 @@ export default class ParcelId { return rest()(this._path); }; - typedPathString: Function = (): string => { - return this._typedPath.join("."); - }; - push: Function = (key: Key, isElement: boolean): ParcelId => { let escapedKey = escapeKey(key); let escapeAndPush: Function = isElement @@ -58,7 +51,6 @@ export default class ParcelId { this.toJS(), update('id', escapeAndPush), update('path', push(key)), - update('typedPath', escapeAndPush), this._create ); }; @@ -73,11 +65,6 @@ export default class ParcelId { toJS: Function = (): Object => ({ id: this._id, - path: this._path, - typedPath: this._typedPath + path: this._path }); - - setTypeCode: Function = (typeCode: string) => { - this._typedPath = update(-1, ii =>`${ii}:${typeCode}`)(this._typedPath); - }; } diff --git a/packages/dataparcels/src/parcelShape/ParcelShape.js b/packages/dataparcels/src/parcelShape/ParcelShape.js new file mode 100644 index 00000000..d605f125 --- /dev/null +++ b/packages/dataparcels/src/parcelShape/ParcelShape.js @@ -0,0 +1,212 @@ +// @flow +import type {Index} from '../types/Types'; +import type {Key} from '../types/Types'; +import type {ParcelData} from '../types/Types'; +import type {ParentType} from '../types/Types'; +import type {ParcelShapeValueUpdater} from '../types/Types'; +import type {ParcelShapeConfigInternal} from '../types/Types'; +import type {ParcelShapeUpdater} from '../types/Types'; +import type {ParcelShapeUpdateFunction} from '../types/Types'; +import type {ParcelShapeSetMeta} from '../types/Types'; + +import Types from '../types/Types'; +import {ReadOnlyError} from '../errors/Errors'; + +import ParcelTypes from '../parcel/ParcelTypes'; +import ParcelId from '../parcelId/ParcelId'; + +import ParcelShapeParentGetMethods from './methods/ParcelShapeParentGetMethods'; +import ParcelShapeParentSetMethods from './methods/ParcelShapeParentSetMethods'; +import ParcelShapeSetMethods from './methods/ParcelShapeSetMethods'; +import ParcelShapeIndexedSetMethods from './methods/ParcelShapeIndexedSetMethods'; + +import FilterMethods from '../util/FilterMethods'; + +import overload from 'unmutable/lib/util/overload'; +import pipeWith from 'unmutable/lib/util/pipeWith'; + +import prepareChildKeys from '../parcelData/prepareChildKeys'; + +export default class ParcelShape { + constructor(value: any, _configInternal: ?ParcelShapeConfigInternal) { + this._parcelData = { + value + }; + + let {parent} = _configInternal || {}; + + // parent + if(parent) { + // $FlowFixMe + this._parent = parent; + } + + // types + this._parcelTypes = new ParcelTypes( + value, + parent && parent._parcelTypes, + !parent + ); + + // methods + this._methods = { + // $FlowFixMe + ...FilterMethods("Parent", ParcelShapeParentGetMethods)(this), + // $FlowFixMe + ...FilterMethods("Parent", ParcelShapeParentSetMethods)(this), + // $FlowFixMe + ...ParcelShapeSetMethods(this), + // $FlowFixMe + ...FilterMethods("Indexed", ParcelShapeIndexedSetMethods)(this) + }; + } + + // + // private + // + + // from constructor + _id: ParcelId; + _methods: { [key: string]: any }; + _parent: ?ParcelShape; + _parcelData: ParcelData; + _parcelTypes: ParcelTypes; + + _pipeSelf = (fn: Function, _configInternal: ?ParcelShapeConfigInternal): ParcelShape => pipeWith( + this._parcelData, + fn, + data => ParcelShape.fromData(data, _configInternal) + ); + + _isParcelShape = (maybe: any): boolean => maybe instanceof ParcelShape; + + _prepareChildKeys = () => { + // prepare child keys only once per parcel instance + // by preparing them and mutating this.parcelData + + if(!this._parcelData.child) { + this._parcelData = prepareChildKeys()(this._parcelData); + } + } + + // + // getters + // + + // $FlowFixMe - this doesn't have side effects + get data(): ParcelData { + return this._parcelData; + } + + // $FlowFixMe - this doesn't have side effects + set data(value: any) { + throw ReadOnlyError(); + } + + // $FlowFixMe - this doesn't have side effects + get value(): any { + return this._parcelData.value; + } + + // $FlowFixMe - this doesn't have side effects + set value(value: any) { + throw ReadOnlyError(); + } + + // $FlowFixMe - this doesn't have side effects + get meta(): any { + let {meta = {}} = this._parcelData; + return {...meta}; + } + + // $FlowFixMe - this doesn't have side effects + set meta(value: any) { + throw ReadOnlyError(); + } + + // $FlowFixMe - this doesn't have side effects + get key(): Key { + return this._parcelData.key; + } + + // $FlowFixMe - this doesn't have side effects + set key(value: any) { + throw ReadOnlyError(); + } + + // + // public methods + // + + static fromData(parcelData: ParcelData, _configInternal: ?ParcelShapeConfigInternal): ParcelShape { + Types(`ParcelShape()`, `fromData`, `parcelData`)(parcelData); + let parcelShape = new ParcelShape(parcelData.value, _configInternal); + parcelShape._parcelData = parcelData; + return parcelShape; + } + + static update(updater: ParcelShapeUpdater): ParcelShapeUpdateFunction { + let fn = (parcelData: ParcelData): ParcelData => { + return ParcelShape + .fromData(parcelData) + .updateShape(updater) + .data; + }; + + fn._isParcelUpdater = true; + fn._updater = updater; + return fn; + } + + // only need this to reference static methods on ParcelShape + // without creating circular dependencies + _parcelShapeUpdate = ParcelShape.update; + + // Parent methods + has = (key: Key|Index): boolean => this._methods.has(key); + size = (): number => this._methods.size(); + get = (key: Key|Index, notFoundValue: ?any = undefined): ParcelShape => this._methods.get(key, notFoundValue); + getIn = (keyPath: Array, notFoundValue: ?any = undefined): ParcelShape => this._methods.getIn(keyPath, notFoundValue); + children = (): ParentType => this._methods.children(); + toObject = (): { [key: string]: ParcelShape } => this._methods.toObject(); + toArray = (): Array => this._methods.toArray(); + + // Change methods + set = overload({ + ["1"]: (value: any) => this._methods.setSelf(value), + ["2"]: (key: Key|Index, value: any) => this.setIn([key], value) + }); + setMeta = (partialMeta: ParcelShapeSetMeta) => this._methods.setMeta(partialMeta); + setIn = (keyPath: Array, value: any) => this._methods.setIn(keyPath, value); + delete = (key: Key|Index) => this.deleteIn([key]); + deleteIn = (keyPath: Array) => this._methods.deleteIn(keyPath); + update = overload({ + ["1"]: (updater: ParcelShapeValueUpdater): ParcelShape => this._methods.update(updater), + ["2"]: (key: Key|Index, updater: ParcelShapeValueUpdater): ParcelShape => this.updateIn([key], updater) + }); + updateShape = overload({ + ["1"]: (updater: ParcelShapeUpdater): ParcelShape => this._methods.updateShape(updater), + ["2"]: (key: Key|Index, updater: ParcelShapeUpdater): ParcelShape => this.updateShapeIn([key], updater) + }); + updateIn = (keyPath: Array, updater: ParcelShapeValueUpdater) => this._methods.updateIn(keyPath, updater); + updateShapeIn = (keyPath: Array, updater: ParcelShapeValueUpdater) => this._methods.updateShapeIn(keyPath, updater); + + // Indexed methods + insertAfter = (key: Key|Index, value: any) => this._methods.insertAfter(key, value); + insertBefore = (key: Key|Index, value: any) => this._methods.insertBefore(key, value); + move = (keyA: Key|Index, keyB: Key|Index) => this._methods.move(keyA, keyB); + push = (...values: Array) => this._methods.push(...values); + pop = () => this._methods.pop(); + shift = () => this._methods.shift(); + swap = (keyA: Key|Index, keyB: Key|Index) => this._methods.swap(keyA, keyB); + swapNext = (key: Key|Index) => this._methods.swapNext(key); + swapPrev = (key: Key|Index) => this._methods.swapPrev(key); + unshift = (...values: Array) => this._methods.unshift(...values); + + // Type methods + isChild = (): boolean => this._parcelTypes.isChild(); + isElement = (): boolean => this._parcelTypes.isElement(); + isIndexed = (): boolean => this._parcelTypes.isIndexed(); + isParent = (): boolean => this._parcelTypes.isParent(); + isTopLevel = (): boolean => this._parcelTypes.isTopLevel(); +} diff --git a/packages/dataparcels/src/parcelShape/__test__/ParcelShape-test.js b/packages/dataparcels/src/parcelShape/__test__/ParcelShape-test.js new file mode 100644 index 00000000..deaf1e95 --- /dev/null +++ b/packages/dataparcels/src/parcelShape/__test__/ParcelShape-test.js @@ -0,0 +1,60 @@ +// @flow +import ParcelShape from '../ParcelShape'; + +test('ParcelShapes should accept value', () => { + let parcel = new ParcelShape(123); + expect(parcel.value).toEqual(123); +}); + +test('ParcelShapes.fromData should accept parcel data', () => { + let data = { + value: [0,1,2], + meta: { + abc: 123 + }, + key: "^", + child: [ + {key: "#a"}, + {key: "#b"}, + {key: "#c"} + ] + }; + + let parcel = ParcelShape.fromData(data); + expect(parcel.data).toEqual(data); +}); + +test('ParcelShapes should return data', () => { + let parcel = new ParcelShape(123); + expect(parcel.data).toEqual({ + value: 123 + }); +}); + +test('ParcelShapes should return empty meta object', () => { + let parcel = new ParcelShape(123); + expect(parcel.meta).toEqual({}); +}); + +test('ParcelShapes should throw errors when attempted to set getters', () => { + let readOnly = 'This property is read-only'; + + let parcel = new ParcelShape(123); + + expect(() => { + parcel.data = 123; + }).toThrow(readOnly); + + expect(() => { + parcel.value = 123; + }).toThrow(readOnly); + + expect(() => { + parcel.meta = 123; + }).toThrow(readOnly); + + expect(() => { + parcel.key = 123; + }).toThrow(readOnly); + +}); diff --git a/packages/dataparcels/src/parcelShape/__test__/ParcelShapeIndexedSetMethods-test.js b/packages/dataparcels/src/parcelShape/__test__/ParcelShapeIndexedSetMethods-test.js new file mode 100644 index 00000000..37db1ed7 --- /dev/null +++ b/packages/dataparcels/src/parcelShape/__test__/ParcelShapeIndexedSetMethods-test.js @@ -0,0 +1,82 @@ +// @flow +import ParcelShape from '../ParcelShape'; + +test('ParcelShapes insertAfter(key, value) should work', () => { + let staticParcel = ParcelShape.fromData({ + value: [0,1,2] + }); + + expect(staticParcel.insertAfter(0, 3).data.value).toEqual([0,3,1,2]); +}); + +test('ParcelShapes insertBefore(key, value) should work', () => { + let staticParcel = ParcelShape.fromData({ + value: [0,1,2] + }); + + expect(staticParcel.insertBefore(0, 3).data.value).toEqual([3,0,1,2]); +}); + +test('ParcelShapes move() should work', () => { + let staticParcel = ParcelShape.fromData({ + value: [0,1,2] + }); + + expect(staticParcel.move(2,0).data.value).toEqual([2,0,1]); +}); + +test('ParcelShapes push(value) should work', () => { + let staticParcel = ParcelShape.fromData({ + value: [0,1,2] + }); + + expect(staticParcel.push(3,4).data.value).toEqual([0,1,2,3,4]); +}); + +test('ParcelShapes pop() should work', () => { + let staticParcel = ParcelShape.fromData({ + value: [0,1,2] + }); + + expect(staticParcel.pop().data.value).toEqual([0,1]); +}); + +test('ParcelShapes shift() should work', () => { + let staticParcel = ParcelShape.fromData({ + value: [0,1,2] + }); + + expect(staticParcel.shift().data.value).toEqual([1,2]); +}); + +test('ParcelShapes swap() should work', () => { + let staticParcel = ParcelShape.fromData({ + value: [0,1,2] + }); + + expect(staticParcel.swap(0,2).data.value).toEqual([2,1,0]); +}); + +test('ParcelShapes swapNext() should work', () => { + let staticParcel = ParcelShape.fromData({ + value: [0,1,2] + }); + + expect(staticParcel.swapNext(1).data.value).toEqual([0,2,1]); +}); + +test('ParcelShapes swapPrev() should work', () => { + let staticParcel = ParcelShape.fromData({ + value: [0,1,2] + }); + + expect(staticParcel.swapPrev(1).data.value).toEqual([1,0,2]); +}); + +test('ParcelShapes unshift(value) should work', () => { + let staticParcel = ParcelShape.fromData({ + value: [0,1,2] + }); + + expect(staticParcel.unshift(3,4).data.value).toEqual([3,4,0,1,2]); +}); diff --git a/packages/dataparcels/src/parcelShape/__test__/ParcelShapeParentGetMethods-test.js b/packages/dataparcels/src/parcelShape/__test__/ParcelShapeParentGetMethods-test.js new file mode 100644 index 00000000..06678df0 --- /dev/null +++ b/packages/dataparcels/src/parcelShape/__test__/ParcelShapeParentGetMethods-test.js @@ -0,0 +1,147 @@ +// @flow +import ParcelShape from '../ParcelShape'; + +import map from 'unmutable/lib/map'; + +test('ParcelShapes getters should work', () => { + let data = { + value: [0,1,2], + meta: { + abc: 123 + }, + key: "^", + child: [ + {key: "#a"}, + {key: "#b"}, + {key: "#c"} + ] + }; + + let parcel = ParcelShape.fromData(data); + expect(parcel.meta).toEqual(data.meta); + expect(parcel.key).toEqual(data.key); +}); + +test('ParcelShapes.get should work with objects', () => { + let parcelShape = ParcelShape.fromData({ + value: { + a: { + b: 1 + } + } + }); + + let expectedParcelData = { + value: { + b: 1 + }, + key: "a" + }; + + expect(parcelShape.get('a').data).toEqual(expectedParcelData); +}); + +test('ParcelShapes.getIn should work with objects', () => { + let parcelShape = ParcelShape.fromData({ + value: { + a: { + b: 1 + } + } + }); + + let expectedParcelData = { + value: 1, + key: "b" + }; + + expect(parcelShape.getIn(['a','b']).data).toEqual(expectedParcelData); +}); + +test('ParcelShapes.getIn should work with objects with non-existent keys', () => { + let parcelShape = ParcelShape.fromData({ + value: { + a: { + b: 1 + } + } + }); + + let expectedParcelData = { + value: undefined, + key: "z" + }; + + expect(parcelShape.getIn(['z']).data).toEqual(expectedParcelData); +}); + +test('ParcelShapes.getIn should return undefined if asking through a non-parent', () => { + let parcelShape = ParcelShape.fromData({ + value: { + a: 1 + } + }); + + let expectedParcelData = { + value: undefined, + key: "b" + }; + + expect(parcelShape.getIn(['a', 'b']).data).toEqual(expectedParcelData); +}); + +test('ParcelShapes.size() should return size of parcel', () => { + let parcelShape = ParcelShape.fromData({ + value: { + a: 1, + b: 4 + } + }); + + expect(parcelShape.size()).toBe(2); +}); + +test('ParcelShapes.has(key) should return a boolean indicating if key exists', () => { + let parcelShape = ParcelShape.fromData({ + value: { + a: 1, + b: 4 + } + }); + + expect(parcelShape.has('a')).toBe(true); + expect(parcelShape.has('z')).toBe(false); +}); + +test('ParcelShapes.children() should make a parent data type full of parcelShapes', () => { + let children = ParcelShape.fromData({ + value: {a:1,b:2,c:3} + }) + .children(); + + expect(map(ii => ii.value)(children)).toEqual({a:1,b:2,c:3}); +}); + +test('ParcelShapes.toObject() should make an object', () => { + let parcelShape = ParcelShape.fromData({ + value: {a:1,b:2,c:3}, + meta: { + a: {a:4,b:5,c:6} + } + }); + + var expectedValue = {a:1,b:2,c:3}; + expect(map(ii => ii.value)(parcelShape.toObject())).toEqual(expectedValue); +}); + +test('ParcelShapes.toArray() should make an array', () => { + let parcelShape = ParcelShape.fromData({ + value: {a:1,b:2,c:3}, + meta: { + a: {a:4,b:5,c:6} + } + }); + + var expectedValue = [1,2,3]; + expect(map(ii => ii.value)(parcelShape.toArray())).toEqual(expectedValue); +}); diff --git a/packages/dataparcels/src/parcelShape/__test__/ParcelShapeParentSetMethods-test.js b/packages/dataparcels/src/parcelShape/__test__/ParcelShapeParentSetMethods-test.js new file mode 100644 index 00000000..205f587d --- /dev/null +++ b/packages/dataparcels/src/parcelShape/__test__/ParcelShapeParentSetMethods-test.js @@ -0,0 +1,236 @@ +// @flow +import ParcelShape from '../ParcelShape'; +import TestValidateValueUpdater from '../../util/__test__/TestValidateValueUpdater-testUtil'; + +test('ParcelShapes set(key) should work', () => { + let parcelShape = ParcelShape.fromData({ + value: { + a: 1, + b: 4 + }, + child: { + a: {key: "a"}, + b: {key: "b"} + } + }); + + let expectedData = { + value: { + a: 456, + b: 4 + }, + child: { + a: { + child: undefined, + key: "a" + }, + b: { + key: "b" + } + } + }; + + expect(parcelShape.set('a', 456).data).toEqual(expectedData); +}); + +test('ParcelShapes setIn(keyPath) should work', () => { + let parcelShape = ParcelShape.fromData({ + value: { + a: { + b: 123 + }, + c: 456 + } + }); + + let expectedData = { + value: { + a: { + b: 456 + }, + c: 456 + }, + child: { + a: { + child: { + b: { + child: undefined, + key: "b" + } + }, + key: "a" + }, + c: { + key: "c" + } + } + }; + + expect(parcelShape.setIn(['a', 'b'], 456).data).toEqual(expectedData); +}); + +test('ParcelShapes delete(key) should work', () => { + let parcelShape = ParcelShape.fromData({ + value: { + a: 1, + b: 4 + }, + child: { + a: {key: "a"}, + b: {key: "b"} + } + }); + + let expectedData = { + value: { + b: 4 + }, + child: { + b: { + key: "b" + } + } + }; + + expect(parcelShape.delete('a').data).toEqual(expectedData); +}); + +test('ParcelShapes deleteIn(keyPath) should work', () => { + let parcelShape = ParcelShape.fromData({ + value: { + a: { + b: 123 + }, + c: 456 + } + }); + + let expectedData = { + value: { + a: {}, + c: 456 + }, + child: { + a: { + child: {}, + key: "a" + }, + c: { + key: "c" + } + } + }; + + expect(parcelShape.deleteIn(['a', 'b']).data).toEqual(expectedData); +}); + +test('ParcelShapes update(key) should work', () => { + let parcelShape = ParcelShape.fromData({ + value: { + a: 1, + b: 4 + } + }); + + let expectedData = { + value: { + a: 2, + b: 4 + }, + child: { + a: { + child: undefined, + key: "a" + }, + b: { + child: undefined, + key: "b" + } + } + }; + + expect(parcelShape.update('a', ii => ii + 1).data).toEqual(expectedData); +}); + +test('ParcelShapes update(key) should validate value updater', () => { + TestValidateValueUpdater( + expect, + (value, updater) => new ParcelShape({ + abc: value + }).update('abc', updater) + ); +}); + +test('ParcelShapes updateIn(keyPath) should work', () => { + let parcelShape = ParcelShape.fromData({ + value: { + a: { + b: 123 + } + } + }); + + let expectedData = { + value: { + a: { + b: 124 + } + }, + child: { + a: { + child: { + b: { + child: undefined, + key: "b" + } + }, + key: "a" + } + } + }; + + expect(parcelShape.updateIn(['a', 'b'], ii => ii + 1).data).toEqual(expectedData); +}); + +test('ParcelShapes updateIn(keyPath) should validate value updater', () => { + TestValidateValueUpdater( + expect, + (value, updater) => new ParcelShape({ + a: { + b: value + } + }).updateIn(['a', 'b'], updater) + ); +}); + +test('ParcelShapes updateShape(key) should work', () => { + let parcelShape = ParcelShape.fromData({ + value: { + abc: [1,2,3] + } + }); + + let expectedValue = { + abc: [1,2,3,4] + }; + + expect(parcelShape.updateShape('abc', parcelShape => parcelShape.push(4)).value).toEqual(expectedValue); +}); + +test('ParcelShapes updateShapeIn(keyPath) should work', () => { + let parcelShape = ParcelShape.fromData({ + value: { + abc: { + def: [1,2,3] + } + } + }); + + let expectedValue = { + abc: { + def: [1,2,3,4] + } + }; + + expect(parcelShape.updateShapeIn(['abc', 'def'], parcelShape => parcelShape.push(4)).value).toEqual(expectedValue); +}); diff --git a/packages/dataparcels/src/parcelShape/__test__/ParcelShapeSetMethods-test.js b/packages/dataparcels/src/parcelShape/__test__/ParcelShapeSetMethods-test.js new file mode 100644 index 00000000..2d267680 --- /dev/null +++ b/packages/dataparcels/src/parcelShape/__test__/ParcelShapeSetMethods-test.js @@ -0,0 +1,230 @@ +// @flow +import ParcelShape from '../ParcelShape'; +import TestValidateValueUpdater from '../../util/__test__/TestValidateValueUpdater-testUtil'; + +test('ParcelShapes set() should work', () => { + let parcelShape = ParcelShape.fromData({ + value: { + a: 1, + b: 4 + }, + child: { + a: {key: "a"}, + b: {key: "b"} + } + }); + + let expectedData = { + value: 456 + }; + + expect(parcelShape.set(456).data).toEqual(expectedData); +}); + +test('ParcelShapes setMeta(partialMeta) should work', () => { + let parcelShape = ParcelShape.fromData({ + value: 123, + meta: { + abc: 123 + } + }); + + let expectedData = { + value: 123, + meta: { + abc: 123, + def: 456 + } + }; + + expect(parcelShape.setMeta({def: 456}).data).toEqual(expectedData); +}); + +test('ParcelShapes setMeta(updater) should work', () => { + let updater = jest.fn(meta => ({def: 456})); + + let parcelShape = ParcelShape.fromData({ + value: 123, + meta: { + abc: 123 + } + }); + + let expectedData = { + value: 123, + meta: { + abc: 123, + def: 456 + } + }; + + let {data} = parcelShape.setMeta(updater); + + expect(updater.mock.calls[0][0]).toEqual({ + abc: 123 + }); + + expect(data).toEqual(expectedData); +}); + +test('ParcelShapes update() should work', () => { + let parcelShape = ParcelShape.fromData({ + value: 123 + }); + + let expectedData = { + value: 124 + }; + + expect(parcelShape.update(ii => ii + 1).data).toEqual(expectedData); +}); + +test('ParcelShapes update() should validate value updater', () => { + TestValidateValueUpdater( + expect, + (value, updater) => new ParcelShape(value).update(updater) + ); +}); + +test('ParcelShapes updateShape() should work with returned ParcelShape', () => { + let parcelShape = ParcelShape.fromData({ + value: 123, + meta: { + abc: 789 + }, + key: "z" + }); + + let expectedData = { + value: 456, + meta: { + abc: 789 + }, + key: "z" + }; + + expect(parcelShape.updateShape(parcelShape => parcelShape.set(456)).data).toEqual(expectedData); +}); + +test('ParcelShapes updateShape() should work with returned primitive', () => { + let parcelShape = ParcelShape.fromData({ + value: 123, + meta: { + abc: 789 + }, + key: "z" + }); + + let expectedData = { + value: 456, + meta: { + abc: 789 + }, + key: "z" + }; + + expect(parcelShape.updateShape(() => 456).data).toEqual(expectedData); +}); + +test('ParcelShapes updateShape() should work with returned parent value', () => { + let parcelShape = ParcelShape.fromData({ + value: [1,2,3], + meta: { + abc: 789 + }, + key: "z" + }); + + let expectedData = { + value: [2,3,4], + meta: { + abc: 789 + }, + child: [ + {key: "#a", child: undefined}, + {key: "#b", child: undefined}, + {key: "#c", child: undefined} + ], + key: "z" + }; + + let {data} = parcelShape.updateShape((parcelShape) => { + return parcelShape + .children() + .map((child => child.update(value => value + 1))) + }); + + expect(data).toEqual(expectedData); +}); + +test('ParcelShapes updateShape() should work with returned parent value of different type', () => { + let parcelShape = ParcelShape.fromData({ + value: { + abc: 123, + def: 456 + }, + meta: { + abc: 789 + }, + key: "z" + }); + + let expectedData = { + value: [ + 123, + 456 + ], + meta: { + abc: 789 + }, + child: [ + {key: "#a", child: undefined}, + {key: "#b", child: undefined} + ], + key: "z" + }; + + let {data} = parcelShape.updateShape((parcelShape) => parcelShape.toArray()); + + expect(data).toEqual(expectedData); +}); + +test('ParcelShapes updateShape() should retain childs keys', () => { + let parcelShape = ParcelShape.fromData({ + value: ["a","b","c","d"], + child: [ + {key: "#a", child: undefined}, + {key: "#b", child: undefined, meta: {abc: 123}}, + {key: "#c", child: undefined}, + {key: "#d", child: undefined} + ] + }); + + let expectedData = { + value: ["b","d"], + child: [ + {key: "#b", child: undefined, meta: {abc: 123}}, + {key: "#d", child: undefined} + ] + }; + + let {data} = parcelShape.updateShape((parcelShape) => { + return parcelShape + .toArray() + .filter((value, key) => key % 2 === 1) + }); + + expect(data).toEqual(expectedData); +}); + +test('ParcelShapes updateShape() should throw error if non ParcelShape is a child of the return value', () => { + let parcelShape = ParcelShape.fromData({ + value: [123, 456] + }); + + expect(() => parcelShape.updateShape((parcelShape) => { + let arr = parcelShape.toArray(); + arr.push(789); + return arr; + })).toThrow('Every child value on a collection returned from a shape updater must be a ParcelShape'); +}); diff --git a/packages/dataparcels/src/parcelShape/__test__/ParcelShapeTypes-test.js b/packages/dataparcels/src/parcelShape/__test__/ParcelShapeTypes-test.js new file mode 100644 index 00000000..dd176513 --- /dev/null +++ b/packages/dataparcels/src/parcelShape/__test__/ParcelShapeTypes-test.js @@ -0,0 +1,171 @@ +// @flow +import {Map, List} from 'immutable'; +import ParcelShape from '../ParcelShape'; + +test('ParcelShapeTypes should correctly identify primitive values', () => { + var value = 123; + expect(new ParcelShape(value).isParent()).toBe(false); + expect(new ParcelShape(value).isIndexed()).toBe(false); + expect(new ParcelShape(value).isChild()).toBe(false); + expect(new ParcelShape(value).isElement()).toBe(false); + expect(new ParcelShape(value).isTopLevel()).toBe(true); +}); + +test('ParcelShapeTypes should correctly identify date', () => { + var value = new Date(); + expect(new ParcelShape(value).isParent()).toBe(false); + expect(new ParcelShape(value).isIndexed()).toBe(false); + expect(new ParcelShape(value).isChild()).toBe(false); + expect(new ParcelShape(value).isElement()).toBe(false); + expect(new ParcelShape(value).isTopLevel()).toBe(true); +}); + +test('ParcelShapeTypes should correctly identify object values', () => { + var value = { + a: "A" + }; + expect(new ParcelShape(value).isParent()).toBe(true); + expect(new ParcelShape(value).isIndexed()).toBe(false); + expect(new ParcelShape(value).isChild()).toBe(false); + expect(new ParcelShape(value).isElement()).toBe(false); + expect(new ParcelShape(value).isTopLevel()).toBe(true); +}); + +test('ParcelShapeTypes should correctly identify class instance values', () => { + class Thing { + foo = "123" + } + var value = new Thing(); + expect(new ParcelShape(value).isParent()).toBe(false); + expect(new ParcelShape(value).isIndexed()).toBe(false); + expect(new ParcelShape(value).isChild()).toBe(false); + expect(new ParcelShape(value).isElement()).toBe(false); + expect(new ParcelShape(value).isTopLevel()).toBe(true); +}); + +test('ParcelShapeTypes should correctly identify unmutable compatible class instance values', () => { + class UnmutableCompatible { + __UNMUTABLE_COMPATIBLE__ = true; + foo = "123"; + } + var value = new UnmutableCompatible(); + expect(new ParcelShape(value).isParent()).toBe(true); + expect(new ParcelShape(value).isIndexed()).toBe(false); + expect(new ParcelShape(value).isChild()).toBe(false); + expect(new ParcelShape(value).isElement()).toBe(false); + expect(new ParcelShape(value).isTopLevel()).toBe(true); +}); + + +test('ParcelShapeTypes should correctly identify Immutable.js Map values', () => { + var value = Map({ + a: "A" + }); + expect(new ParcelShape(value).isParent()).toBe(true); + expect(new ParcelShape(value).isIndexed()).toBe(false); + expect(new ParcelShape(value).isChild()).toBe(false); + expect(new ParcelShape(value).isElement()).toBe(false); + expect(new ParcelShape(value).isTopLevel()).toBe(true); +}); + + +test('ParcelShapeTypes should correctly identify array values', () => { + var value = [1,2,3]; + expect(new ParcelShape(value).isParent()).toBe(true); + expect(new ParcelShape(value).isIndexed()).toBe(true); + expect(new ParcelShape(value).isChild()).toBe(false); + expect(new ParcelShape(value).isElement()).toBe(false); + expect(new ParcelShape(value).isTopLevel()).toBe(true); +}); + +test('ParcelShapeTypes should correctly identify Immutable.js List values', () => { + var value = List([1,2,3]); + expect(new ParcelShape(value).isParent()).toBe(true); + expect(new ParcelShape(value).isIndexed()).toBe(true); + expect(new ParcelShape(value).isChild()).toBe(false); + expect(new ParcelShape(value).isElement()).toBe(false); + expect(new ParcelShape(value).isTopLevel()).toBe(true); +}); + +test('ParcelShapeTypes should correctly identify child values', () => { + var value = { + a: "A" + }; + expect(new ParcelShape(value).get("a").isParent()).toBe(false); + expect(new ParcelShape(value).get("a").isIndexed()).toBe(false); + expect(new ParcelShape(value).get("a").isChild()).toBe(true); + expect(new ParcelShape(value).get("a").isElement()).toBe(false); + expect(new ParcelShape(value).get("a").isTopLevel()).toBe(false); +}); + +test('ParcelShapeTypes should correctly identify element values', () => { + var value = [1,2,3]; + expect(new ParcelShape(value).get(0).isParent()).toBe(false); + expect(new ParcelShape(value).get(0).isIndexed()).toBe(false); + expect(new ParcelShape(value).get(0).isChild()).toBe(true); + expect(new ParcelShape(value).get(0).isElement()).toBe(true); + expect(new ParcelShape(value).get(0).isTopLevel()).toBe(false); +}); + +test('ParcelShapeTypes should correctly identify child values with getIn', () => { + var value = { + b: { + a: "A" + } + }; + expect(new ParcelShape(value).getIn(["b","a"]).isParent()).toBe(false); + expect(new ParcelShape(value).getIn(["b","a"]).isIndexed()).toBe(false); + expect(new ParcelShape(value).getIn(["b","a"]).isChild()).toBe(true); + expect(new ParcelShape(value).getIn(["b","a"]).isElement()).toBe(false); + expect(new ParcelShape(value).getIn(["b","a"]).isTopLevel()).toBe(false); +}); + +test('ParcelShapeTypes should correctly identify element values with getIn', () => { + var value = { + b: [1,2,3] + }; + + expect(new ParcelShape(value).getIn(["b",0]).isParent()).toBe(false); + expect(new ParcelShape(value).getIn(["b",0]).isIndexed()).toBe(false); + expect(new ParcelShape(value).getIn(["b",0]).isChild()).toBe(true); + expect(new ParcelShape(value).getIn(["b",0]).isElement()).toBe(true); + expect(new ParcelShape(value).getIn(["b",0]).isTopLevel()).toBe(false); +}); + +// method creators + +test('Correct methods are created for primitive values', () => { + var value = 123; + expect(() => new ParcelShape(value).set('A')).not.toThrow(); + expect(() => new ParcelShape(value).has('a')).toThrowError(`.has() is not a function`); + expect(() => new ParcelShape(value).pop()).toThrowError(`.pop() is not a function`); +}); + +test('Correct methods are created for object values', () => { + var value = {a: 123}; + expect(() => new ParcelShape(value).set('A')).not.toThrow(); + expect(() => new ParcelShape(value).has('a')).not.toThrow(); + expect(() => new ParcelShape(value).pop()).toThrowError(`.pop() is not a function`); +}); + +test('Correct methods are created for array values', () => { + var value = [1,2,3]; + expect(() => new ParcelShape(value).set('A')).not.toThrow(); + expect(() => new ParcelShape(value).has('a')).not.toThrow(); + expect(() => new ParcelShape(value).pop()).not.toThrow(); +}); + +test('Correct methods are created for object child values', () => { + var value = {a: 123}; + expect(() => new ParcelShape(value).get("a").set('A')).not.toThrow(); + expect(() => new ParcelShape(value).get("a").has('a')).toThrowError(`.has() is not a function`); + expect(() => new ParcelShape(value).get("a").pop()).toThrowError(`.pop() is not a function`); +}); + +test('Correct methods are created for array element values', () => { + var value = [1,2,3]; + expect(() => new ParcelShape(value).get(0).set('A')).not.toThrow(); + expect(() => new ParcelShape(value).get(0).has('a')).toThrowError(`.has() is not a function`); + expect(() => new ParcelShape(value).get(0).pop()).toThrowError(`.pop() is not a function`); +}); + diff --git a/packages/dataparcels/src/parcelShape/methods/ParcelShapeIndexedSetMethods.js b/packages/dataparcels/src/parcelShape/methods/ParcelShapeIndexedSetMethods.js new file mode 100644 index 00000000..e0cf569c --- /dev/null +++ b/packages/dataparcels/src/parcelShape/methods/ParcelShapeIndexedSetMethods.js @@ -0,0 +1,88 @@ +// @flow +import type {Index} from '../../types/Types'; +import type {Key} from '../../types/Types'; +import type ParcelShape from '../ParcelShape'; + +import insertAfter from '../../parcelData/insertAfter'; +import insertBefore from '../../parcelData/insertBefore'; +import move from '../../parcelData/move'; +import pop from '../../parcelData/pop'; +import push from '../../parcelData/push'; +import shift from '../../parcelData/shift'; +import swap from '../../parcelData/swap'; +import swapNext from '../../parcelData/swapNext'; +import swapPrev from '../../parcelData/swapPrev'; +import unshift from '../../parcelData/unshift'; + +export default (_this: ParcelShape) => ({ + + insertAfter: (key: Key|Index, value: *): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + insertAfter(key, value) + ); + }, + + insertBefore: (key: Key|Index, value: *): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + insertBefore(key, value) + ); + }, + + move: (keyA: Key|Index, keyB: Key|Index): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + move(keyA, keyB) + ); + }, + + pop: (): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + pop() + ); + }, + + push: (...values: Array<*>): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + push(...values) + ); + }, + + shift: (): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + shift() + ); + }, + + swap: (keyA: Key|Index, keyB: Key|Index): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + swap(keyA, keyB) + ); + }, + + swapNext: (key: Key|Index): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + swapNext(key) + ); + }, + + swapPrev: (key: Key|Index): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + swapPrev(key) + ); + }, + + unshift: (...values: Array<*>): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + unshift(...values) + ); + } +}); diff --git a/packages/dataparcels/src/parcelShape/methods/ParcelShapeParentGetMethods.js b/packages/dataparcels/src/parcelShape/methods/ParcelShapeParentGetMethods.js new file mode 100644 index 00000000..2f389461 --- /dev/null +++ b/packages/dataparcels/src/parcelShape/methods/ParcelShapeParentGetMethods.js @@ -0,0 +1,72 @@ +// @flow +import type {Index} from '../../types/Types'; +import type {Key} from '../../types/Types'; +import type {ParentType} from '../../types/Types'; +import type ParcelShape from '../ParcelShape'; + +import parcelGet from '../../parcelData/get'; +import parcelHas from '../../parcelData/has'; +import parcelKeyOrIndexToKey from '../../parcelData/keyOrIndexToKey'; + +import map from 'unmutable/lib/map'; +import size from 'unmutable/lib/size'; +import toArray from 'unmutable/lib/toArray'; +import toObject from 'unmutable/lib/toObject'; +import pipeWith from 'unmutable/lib/util/pipeWith'; + +export default (_this: ParcelShape) => ({ + + has: (key: Key|Index): boolean => { + _this._prepareChildKeys(); + return parcelHas(key)(_this._parcelData); + }, + + size: (): number => { + return size()(_this._parcelData.value); + }, + + get: (key: Key|Index, notFoundValue: ?* = undefined): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + parcelGet(key, notFoundValue), + { + parent: _this + } + ); + }, + + getIn: (keyPath: Array, notFoundValue: ?* = undefined): ParcelShape => { + let parcelShape = _this; + + for(let key of keyPath) { + if(!parcelShape.isParent() || !parcelShape.has(key)) { + return _this._pipeSelf( + () => ({ + value: notFoundValue, + key: parcelKeyOrIndexToKey(key)(parcelShape.data) + }), + { + parent: _this + } + ); + } + parcelShape = parcelShape.get(key, notFoundValue); + } + return parcelShape; + }, + + children: (): ParentType => { + return pipeWith( + _this._parcelData.value, + map((value, key) => _this.get(key)) + ); + }, + + toObject: (): { [key: string]: ParcelShape } => { + return toObject()(_this.children()); + }, + + toArray: (): Array => { + return toArray()(_this.children()); + } +}); diff --git a/packages/dataparcels/src/parcelShape/methods/ParcelShapeParentSetMethods.js b/packages/dataparcels/src/parcelShape/methods/ParcelShapeParentSetMethods.js new file mode 100644 index 00000000..a0b3d5ca --- /dev/null +++ b/packages/dataparcels/src/parcelShape/methods/ParcelShapeParentSetMethods.js @@ -0,0 +1,59 @@ +// @flow +import type {Index} from '../../types/Types'; +import type {Key} from '../../types/Types'; +import type {ParcelShapeUpdater} from '../../types/Types'; +import type {ParcelShapeValueUpdater} from '../../types/Types'; +import type ParcelShape from '../ParcelShape'; + +import parcelDelete from '../../parcelData/delete'; +import parcelSetSelf from '../../parcelData/setSelf'; +import parcelUpdateIn from '../../parcelData/updateIn'; +import ValidateValueUpdater from '../../util/ValidateValueUpdater'; + +import butLast from 'unmutable/lib/butLast'; +import last from 'unmutable/lib/last'; + +export default (_this: ParcelShape) => ({ + + setIn: (keyPath: Array, value: any): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + parcelUpdateIn(keyPath, parcelSetSelf(value)) + ); + }, + + deleteIn: (keyPath: Array): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + parcelUpdateIn( + butLast()(keyPath), + parcelDelete(last()(keyPath)) + ) + ); + }, + + updateIn: (keyPath: Array, updater: ParcelShapeValueUpdater): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + parcelUpdateIn( + keyPath, + (parcelData) => { + let {value} = parcelData; + let updatedValue = updater(value, _this); + ValidateValueUpdater(value, updatedValue); + return parcelSetSelf(updatedValue)(parcelData); + } + ) + ); + }, + + updateShapeIn: (keyPath: Array, updater: ParcelShapeUpdater): ParcelShape => { + _this._prepareChildKeys(); + return _this._pipeSelf( + parcelUpdateIn( + keyPath, + (parcelData) => _this._parcelShapeUpdate(updater)(parcelData) + ) + ); + } +}); diff --git a/packages/dataparcels/src/parcelShape/methods/ParcelShapeSetMethods.js b/packages/dataparcels/src/parcelShape/methods/ParcelShapeSetMethods.js new file mode 100644 index 00000000..187fd92f --- /dev/null +++ b/packages/dataparcels/src/parcelShape/methods/ParcelShapeSetMethods.js @@ -0,0 +1,71 @@ +// @flow +import type ParcelShape from '../ParcelShape'; +import type {ParcelDataEvaluator} from '../../types/Types'; +import type {ParcelShapeValueUpdater} from '../../types/Types'; +import type {ParcelShapeSetMeta} from '../../types/Types'; +import type {ParcelShapeUpdater} from '../../types/Types'; + +import {ShapeUpdaterNonShapeChildError} from '../../errors/Errors'; +import isParentValue from '../../parcelData/isParentValue'; +import parcelSetMeta from '../../parcelData/setMeta'; +import parcelSetSelf from '../../parcelData/setSelf'; +import parcelUpdate from '../../parcelData/update'; +import ValidateValueUpdater from '../../util/ValidateValueUpdater'; + +import clear from 'unmutable/lib/clear'; +import del from 'unmutable/lib/delete'; +import map from 'unmutable/lib/map'; +import set from 'unmutable/lib/set'; +import pipe from 'unmutable/lib/util/pipe'; +import pipeWith from 'unmutable/lib/util/pipeWith'; + +export default (_this: ParcelShape) => ({ + + setSelf: (value: *): ParcelShape => { + return _this._pipeSelf( + parcelSetSelf(value) + ); + }, + + setMeta: (partialMeta: ParcelShapeSetMeta): ParcelShape => { + let meta = typeof partialMeta === "function" + ? partialMeta(_this._parcelData.meta || {}) + : partialMeta; + + return _this._pipeSelf( + parcelSetMeta(meta) + ); + }, + + update: (updater: ParcelShapeValueUpdater): ParcelShape => { + let {value} = _this; + let updatedValue = updater(value, _this); + ValidateValueUpdater(value, updatedValue); + return _this.set(updatedValue); + }, + + updateShape: (updater: ParcelShapeUpdater): ParcelShape => { + let updated: any = updater(_this); + if(_this._isParcelShape(updated)) { + return updated; + } + + if(!isParentValue(updated)) { + return _this.set(updated); + } + + return _this._pipeSelf(pipe( + set('value', clear()(updated)), + del('child'), + ...pipeWith( + updated, + map((childParcelShape: ParcelShape, key: string|number): ParcelDataEvaluator => { + if(!_this._isParcelShape(childParcelShape)) { + throw ShapeUpdaterNonShapeChildError(); + } + return parcelUpdate(key, () => childParcelShape.data); + }) + ) + )); + } +}); diff --git a/packages/dataparcels/src/parcelShape/shape.js b/packages/dataparcels/src/parcelShape/shape.js new file mode 100644 index 00000000..8495e2c8 --- /dev/null +++ b/packages/dataparcels/src/parcelShape/shape.js @@ -0,0 +1,3 @@ +// @flow +import ParcelShape from './ParcelShape'; +export default ParcelShape.update; diff --git a/packages/dataparcels/src/treeshare/Treeshare.js b/packages/dataparcels/src/treeshare/Treeshare.js index 28426511..3ad59cd2 100644 --- a/packages/dataparcels/src/treeshare/Treeshare.js +++ b/packages/dataparcels/src/treeshare/Treeshare.js @@ -12,18 +12,6 @@ class ParcelRegistry { }; } -class DispatchRegistry { - _dispatchedPaths: Object = {}; - - hasPathDispatched = (path: string[]): boolean => { - return !!this._dispatchedPaths[stringifyPath(path)]; - }; - - markPathAsDispatched = (path: string[]) => { - this._dispatchedPaths[stringifyPath(path)] = true; - }; -} - class LocationShareRegistry { _locationShareData: Object = {}; @@ -41,37 +29,27 @@ class LocationShareRegistry { type Config = { registry?: ParcelRegistry, - dispatch?: DispatchRegistry, - locationShare?: LocationShareRegistry, - debugRender: boolean + locationShare?: LocationShareRegistry }; export default class Treeshare { - debugRender: boolean; registry: ParcelRegistry; - dispatch: DispatchRegistry; locationShare: LocationShareRegistry; - constructor(config: Config) { + constructor(config: Config = {}) { let { registry, - dispatch, - locationShare, - debugRender + locationShare } = config; this.registry = registry || new ParcelRegistry(); - this.dispatch = dispatch || new DispatchRegistry(); this.locationShare = locationShare || new LocationShareRegistry(); - this.debugRender = debugRender || false; } boundarySplit: Function = (): Treeshare => { return new Treeshare({ // do not pass in registry - dispatch: this.dispatch, - locationShare: this.locationShare, - debugRender: this.debugRender + locationShare: this.locationShare }); } } diff --git a/packages/dataparcels/src/types/Types.js b/packages/dataparcels/src/types/Types.js index 6e648d3c..b707635b 100644 --- a/packages/dataparcels/src/types/Types.js +++ b/packages/dataparcels/src/types/Types.js @@ -5,6 +5,7 @@ import type Treeshare from '../treeshare/Treeshare'; import Parcel from '../parcel/Parcel'; import Action from '../change/Action'; import ChangeRequest from '../change/ChangeRequest'; +import ParcelShape from '../parcelShape/ParcelShape'; import isPlainObject from 'unmutable/lib/util/isPlainObject'; export type ParcelData = { @@ -14,26 +15,27 @@ export type ParcelData = { meta?: ParcelMeta }; +export type ParcelDataEvaluator = (parcelData: ParcelData) => ParcelData; + export type ParcelConfig = { handleChange?: Function, - value?: *, - debugRender?: boolean + value?: * }; export type ParcelConfigInternal = { onDispatch?: Function, child: *, + lastOriginId: string, meta: ParcelMeta, id: ParcelId, - matchPipes?: MatchPipe[], parent?: ?Parcel, treeshare: Treeshare }; -export type CreateParcelConfigType = { +export type ParcelCreateConfigType = { onDispatch?: Function, + lastOriginId: string, id?: ParcelId, - matchPipes?: MatchPipe[], parcelData?: ParcelData, parent?: ?Parcel, handleChange?: Function, @@ -41,27 +43,28 @@ export type CreateParcelConfigType = { }; export type ParcelMeta = {[key: string]: *}; -export type ParcelMetaUpdater = (meta: ParcelMeta) => ParcelMeta; - -export type ParcelBatcher = (item: Parcel) => void; -export type ParcelMapper = (item: Parcel, index: string|number, _this: Parcel) => *; +export type ParcelMapper = (item: Parcel, property: string|number, parent: Parcel) => *; export type ParcelUpdater = (item: Parcel) => Parcel; -export type ParcelValueUpdater = (value: *) => *; +export type ParcelValueUpdater = (value: *, parcel: Parcel) => any; +export type ParcelShapeUpdateFunction = Function; +export type ParcelShapeUpdater = (item: ParcelShape) => any; -export type MatchPipe = { - match: string, - depth: number, - updater: ParcelUpdater +export type ParcelShapeSetMeta = ParcelMeta | (meta: ParcelMeta) => ParcelMeta; +export type ParcelShapeValueUpdater = (value: *, parcel: ParcelShape) => any; + +export type ParcelShapeConfigInternal = { + parent?: ?ParcelShape }; export type Key = string; export type Index = number; export type Property = number|string; +export type ParentType = any; // should be any parent data type + export type ParcelIdData = { id: string[], - path: string[], - typedPath: string[] + path: string[] }; const RUNTIME_TYPES = { @@ -114,6 +117,10 @@ const RUNTIME_TYPES = { name: "an object containing parcel data {value: *, meta?: {}, key?: *}", check: ii => isPlainObject(ii) && ii.hasOwnProperty('value') }, + ['parcelShape']: { + name: "a ParcelShape", + check: ii => ii instanceof ParcelShape + }, ['string']: { name: "a string", check: ii => typeof ii === "string" diff --git a/packages/dataparcels/src/util/FilterMethods.js b/packages/dataparcels/src/util/FilterMethods.js index 5fe0bd91..200f911d 100644 --- a/packages/dataparcels/src/util/FilterMethods.js +++ b/packages/dataparcels/src/util/FilterMethods.js @@ -1,10 +1,11 @@ // @flow import type Parcel from '../parcel/Parcel'; +import type ParcelShape from '../parcelShape/ParcelShape'; import map from 'unmutable/lib/map'; import pipeWith from 'unmutable/lib/util/pipeWith'; -export default (parcelType: string, methodCreator: Function) => (parcel: Parcel, ...args: Array<*>): { [key: string]: Function } => { +export default (parcelType: string, methodCreator: Function) => (parcel: Parcel|ParcelShape, ...args: Array<*>): { [key: string]: Function } => { let methods: { [key: string]: Function } = methodCreator(parcel, ...args); // $FlowFixMe - I want to do this @@ -15,7 +16,8 @@ export default (parcelType: string, methodCreator: Function) => (parcel: Parcel, return pipeWith( methods, map((value, key) => (...args: Array<*>) => { - let suffix = `(keyPath: [${parcel.path.join(', ')}]).`; + // $FlowFixMe - why do you pay attention to your types more than preceding conditional logic? + let suffix = Array.isArray(parcel.path) ? `(keyPath: [${parcel.path.join(', ')}]).` : ``; if(key.slice(-4) === "Self") { throw new Error(`.${key.slice(0, -4)}() cannot be called with ${args.length} argument${args.length === 1 ? "" : "s"} on a value of "${parcel.value}". ${suffix}`); diff --git a/packages/dataparcels/src/util/FindParcelsMatching.js b/packages/dataparcels/src/util/FindParcelsMatching.js deleted file mode 100644 index b263ea5c..00000000 --- a/packages/dataparcels/src/util/FindParcelsMatching.js +++ /dev/null @@ -1,46 +0,0 @@ -// @flow -import type Parcel from '../parcel/Parcel'; -import {containsWildcard, split} from '../match/Matcher'; - -import flatMap from 'unmutable/lib/flatMap'; -import shallowEquals from 'unmutable/lib/shallowEquals'; -import take from 'unmutable/lib/take'; -import pipeWith from 'unmutable/lib/util/pipeWith'; - -export default (startParcel: Parcel, match: string): Parcel[] => { - let matchParts = split(match); - let {path} = startParcel; - - let baseMatches = pipeWith( - matchParts, - take(path.length), - shallowEquals(path) - ); - - if(!baseMatches) { - return []; - } - - let get = (parcel: Parcel, matchParts: string[]): Parcel[] => { - let [matchPart, ...remainingMatchParts] = matchParts; - - if(!matchPart) { - return [parcel]; - } - if(!parcel.isParent()) { - return []; - } - if(containsWildcard(matchPart)) { - return pipeWith( - parcel.toArray(pp => get(pp, remainingMatchParts)), - flatMap(ii => ii) - ); - } - if(!parcel.has(matchPart)) { - return []; - } - return get(parcel.get(matchPart), remainingMatchParts); - }; - - return get(startParcel, matchParts.slice(path.length)); -}; diff --git a/packages/dataparcels/src/util/ValidateValueUpdater.js b/packages/dataparcels/src/util/ValidateValueUpdater.js new file mode 100644 index 00000000..ed006a2f --- /dev/null +++ b/packages/dataparcels/src/util/ValidateValueUpdater.js @@ -0,0 +1,10 @@ +// @flow +import isParentValue from '../parcelData/isParentValue'; + +import equals from 'unmutable/lib/equals'; + +export default (value: any, updatedValue: any) => { + if(process.env.NODE_ENV !== 'production' && isParentValue(value) && isParentValue(updatedValue) && !equals(value)(updatedValue)) { + console.warn(`Warning: please ensure you do not change the shape of the value, as changing the data shape or moving children within the data shape can cause dataparcels to misplace its keying and meta information!`); /* eslint-disable-line */ + } +}; diff --git a/packages/dataparcels/src/util/__test__/FindParcelsMatching-test.js b/packages/dataparcels/src/util/__test__/FindParcelsMatching-test.js deleted file mode 100644 index 7053731b..00000000 --- a/packages/dataparcels/src/util/__test__/FindParcelsMatching-test.js +++ /dev/null @@ -1,34 +0,0 @@ -// @flow -import Parcel from 'dataparcels'; -import FindParcelsMatching from '../FindParcelsMatching'; - -test(`FindParcelsMatching() should return all parcels matching the match string at or below the start parcel's depth`, () => { - - let p = new Parcel({ - value: { - abc: { - def: 123, - ghi: 456 - }, - jkl: { - mno: 789 - } - } - }); - - expect(["abc"]).toEqual(FindParcelsMatching(p, "abc").map(ii => ii.path.join("."))); - expect(["abc.def"]).toEqual(FindParcelsMatching(p, "abc.def").map(ii => ii.path.join("."))); - expect([]).toEqual(FindParcelsMatching(p, "abc.def.toofar").map(ii => ii.path.join("."))); - expect([]).toEqual(FindParcelsMatching(p, "abc.woo").map(ii => ii.path.join("."))); - expect([]).toEqual( - FindParcelsMatching(p, "asdf%.kasd.asdasd.asd").map(ii => ii.path.join(".")) - ); - expect(["abc.def", "abc.ghi"]).toEqual(FindParcelsMatching(p, "abc.*").map(ii => ii.path.join("."))); - expect(["abc.def"]).toEqual( - FindParcelsMatching(p.get('abc'), "abc.def").map(ii => ii.path.join(".")) - ); - expect([]).toEqual( - FindParcelsMatching(p.get('mno'), "abc.def").map(ii => ii.path.join(".")) - ); - expect(["abc.def","abc.ghi","jkl.mno"]).toEqual(FindParcelsMatching(p, "*.*").map(ii => ii.path.join("."))); -}); diff --git a/packages/dataparcels/src/util/__test__/GetAction-testUtil.js b/packages/dataparcels/src/util/__test__/GetAction-testUtil.js new file mode 100644 index 00000000..7574fe81 --- /dev/null +++ b/packages/dataparcels/src/util/__test__/GetAction-testUtil.js @@ -0,0 +1,13 @@ +// @flow + +import del from 'unmutable/lib/delete'; +import first from 'unmutable/lib/first'; +import method from 'unmutable/lib/method'; +import pipe from 'unmutable/lib/util/pipe'; + +export default pipe( + method('actions')(), + first(), + method('toJS')(), + del('keyPathModifiers') +); diff --git a/packages/dataparcels/src/util/__test__/TestValidateValueUpdater-testUtil.js b/packages/dataparcels/src/util/__test__/TestValidateValueUpdater-testUtil.js new file mode 100644 index 00000000..2a05942d --- /dev/null +++ b/packages/dataparcels/src/util/__test__/TestValidateValueUpdater-testUtil.js @@ -0,0 +1,20 @@ +// @flow +/* eslint-disable */ + +export default (expect: Function, test: Function) => { + + // $FlowFixMe + console.warn = jest.fn(); + + expect(() => test(123, _ => _)).not.toThrowError(); + expect(() => test(123, () => [])).not.toThrowError(); + expect(() => test([], () => 123)).not.toThrowError(); + expect(() => test([1], _ => _)).not.toThrowError(); + + expect(console.warn).not.toHaveBeenCalled(); + + test([1], () => [2]); + + expect(console.warn).toHaveBeenCalled(); + expect(console.warn.mock.calls[0][0]).toBe(`Warning: please ensure you do not change the shape of the value, as changing the data shape or moving children within the data shape can cause dataparcels to misplace its keying and meta information!`); +}; diff --git a/packages/react-dataparcels-drag/README.md b/packages/react-dataparcels-drag/README.md new file mode 100644 index 00000000..4e54d20d --- /dev/null +++ b/packages/react-dataparcels-drag/README.md @@ -0,0 +1,14 @@ +![dataparcels](https://user-images.githubusercontent.com/345320/48319791-4eece200-e666-11e8-8b19-252cd1135ae2.png) + + +[![CircleCI](https://circleci.com/gh/blueflag/dataparcels/tree/master.svg?style=shield)](https://circleci.com/gh/blueflag/dataparcels/tree/master) + +A plugin for [`react-dataparcels`](https://www.npmjs.com/package/react-dataparcels) that adds drag and drop re-ordering of elements, using the wonderful [react-sortable-hoc](https://github.com/clauderic/react-sortable-hoc). + +## Example + +**[See the example](https://dataparcels.blueflag.codes/examples/editing-arrays)** + +## Packages + +Get [`react-dataparcels-drag`](https://www.npmjs.com/package/react-dataparcels-drag). diff --git a/packages/react-dataparcels-drag/__test__/Exports-test.js b/packages/react-dataparcels-drag/__test__/Exports-test.js new file mode 100644 index 00000000..9ed38e32 --- /dev/null +++ b/packages/react-dataparcels-drag/__test__/Exports-test.js @@ -0,0 +1,9 @@ +// @flow + +// dataparcels exports +import Drag from '../src/index'; +import InternalDrag from '../src/Drag'; + +test('index should export Drag', () => { + expect(Drag).toBe(InternalDrag); +}); diff --git a/packages/react-dataparcels-drag/package.json b/packages/react-dataparcels-drag/package.json new file mode 100644 index 00000000..17b927c7 --- /dev/null +++ b/packages/react-dataparcels-drag/package.json @@ -0,0 +1,32 @@ +{ + "name": "react-dataparcels-drag", + "version": "0.18.0-2", + "description": "A plugin for react-dataparcels that adds drag and drop re-ordering of elements.", + "main": "lib/index.js", + "license": "UNLICENSED", + "author": "Damien Clarke", + "repository": { + "type": "git", + "url": "git+https://github.com/blueflag/dataparcels.git" + }, + "files": [ + "lib" + ], + "bugs": { + "url": "https://github.com/blueflag/dataparcels/issues" + }, + "private": false, + "scripts": { + "build": "rm -rf lib && NODE_ENV=production babel src --out-dir lib --ignore '**/__test__/*.js'", + "build-all": "yarn build", + "watch": "yarn run build -w" + }, + "dependencies": { + "babel-runtime": "6.23.0", + "react-sortable-hoc": "1.4.0" + }, + "peerDependencies": { + "react": "16.4.2", + "react-dataparcels": "^0.18.0-2" + } +} diff --git a/packages/react-dataparcels-drag/src/Drag.js b/packages/react-dataparcels-drag/src/Drag.js new file mode 100644 index 00000000..bccba709 --- /dev/null +++ b/packages/react-dataparcels-drag/src/Drag.js @@ -0,0 +1,47 @@ +// @flow + +import type {ComponentType} from 'react'; +import type {Node} from 'react'; +import type Parcel from 'react-dataparcels'; + +import React from 'react'; +import {SortableContainer} from 'react-sortable-hoc'; +import {SortableElement} from 'react-sortable-hoc'; + +type Config = { + element: (parcel: Parcel, rest: *) => Node, + container?: ComponentType<*> +}; + +type Props = { + parcel: Parcel, + onSortEnd?: ({oldIndex: number, newIndex: number}) => void +}; + +export default ({element, container, ...configRest}: Config) => { + let Container = container || 'div'; + let ConfiguredElement = SortableElement(({parcel, ...rest}) => element(parcel, rest)); + let ConfiguredContainer = SortableContainer(({parcel}) => + {parcel.toArray((elementParcel, index) => )} + ); + + return ({parcel, onSortEnd, ...rest}: Props): Node => { + if(!parcel.isIndexed()) { + throw new Error(`react-dataparcels-drag's parcel prop must be of type indexed`); + } + return { + let {oldIndex, newIndex} = param; + parcel.move(oldIndex, newIndex); + onSortEnd && onSortEnd(param); + }} + {...configRest} + {...rest} + />; + }; +}; diff --git a/packages/react-dataparcels-drag/src/__test__/Drag-test.js b/packages/react-dataparcels-drag/src/__test__/Drag-test.js new file mode 100644 index 00000000..beebed12 --- /dev/null +++ b/packages/react-dataparcels-drag/src/__test__/Drag-test.js @@ -0,0 +1,151 @@ +// // @flow +import React from 'react'; +import Parcel from 'react-dataparcels'; +import Drag from '../Drag'; + +test('Drag must pass props correctly', () => { + + let handleChange = jest.fn(); + + let parcel = new Parcel({ + value: [1,2,3], + handleChange + }); + + let MyDrag = Drag({ + element: () =>
+ }); + + // $FlowFixMe + let wrapper = shallow(, {disableLifecycleMethods: true}); + + // first level in + let props1 = wrapper.props(); + expect(props1.parcel).toBe(parcel); + props1.onSortEnd({oldIndex: 0, newIndex: 2}); + expect(handleChange.mock.calls[0][0].value).toEqual([2,3,1]); + + // second level in + let props2 = wrapper.dive().props(); + expect(props2.parcel).toBe(parcel); + + // third level in + let props3 = wrapper.dive().dive().props(); + expect(props3.children.length).toBe(3); +}); + +test('Drag should throw if parcel is not indexed', () => { + + let parcel = new Parcel({ + value: {abc: 123} + }); + + let MyDrag = Drag({ + element: () =>
+ }); + + expect(() => { + // $FlowFixMe + shallow(, {disableLifecycleMethods: true}); + }).toThrow(`react-dataparcels-drag's parcel prop must be of type indexed`); +}); + +test('Drag must accept onSortEnd and still call internal onSortEnd', () => { + + let handleChange = jest.fn(); + + let parcel = new Parcel({ + value: [1,2,3], + handleChange + }); + + let MyDrag = Drag({ + element: () =>
+ }); + + let onSortEnd = jest.fn(); + + // $FlowFixMe + let wrapper = shallow(, {disableLifecycleMethods: true}); + + let props = wrapper.props(); + let sortEndArg = {oldIndex: 0, newIndex: 2}; + + expect(props.parcel).toBe(parcel); + props.onSortEnd(sortEndArg); + expect(handleChange.mock.calls[0][0].value).toEqual([2,3,1]); + expect(onSortEnd.mock.calls[0][0]).toEqual(sortEndArg); +}); + + +test('Drag must accept additional props and pass them to react-sortable-hoc as props', () => { + + let parcel = new Parcel({ + value: [1,2,3] + }); + + let MyDrag = Drag({ + element: () =>
+ }); + + // $FlowFixMe + let wrapper = shallow(, {disableLifecycleMethods: true}); + + let props = wrapper.props(); + expect(props.woo).toBe(123); +}); + +test('Drag must accept additional config and pass them to react-sortable-hoc as props', () => { + + let parcel = new Parcel({ + value: [1,2,3] + }); + + let MyDrag = Drag({ + element: () =>
, + woo: 123 + }); + + // $FlowFixMe + let wrapper = shallow(, {disableLifecycleMethods: true}); + + let props = wrapper.props(); + expect(props.woo).toBe(123); +}); + +test('Drag must prefer props over config', () => { + + let parcel = new Parcel({ + value: [1,2,3] + }); + + let MyDrag = Drag({ + element: () =>
, + woo: 123 + }); + + // $FlowFixMe + let wrapper = shallow(, {disableLifecycleMethods: true}); + + let props = wrapper.props(); + expect(props.woo).toBe(456); +}); + +test('Drag must render elements and pass parcels to them', () => { + + let value = [1,2,3]; + + let parcel = new Parcel({ + value + }); + + let element = jest.fn(() =>
); + + let MyDrag = Drag({ + element + }); + + // $FlowFixMe + let wrapper = mount(); + expect(element.mock.calls.map(call => call[0].value)).toEqual([1,2,3]); +}); diff --git a/packages/react-dataparcels-drag/src/index.js b/packages/react-dataparcels-drag/src/index.js new file mode 100644 index 00000000..a5099964 --- /dev/null +++ b/packages/react-dataparcels-drag/src/index.js @@ -0,0 +1,4 @@ +// @flow + +import Drag from './Drag'; +export default Drag; diff --git a/packages/react-dataparcels/Action.js b/packages/react-dataparcels/Action.js new file mode 100644 index 00000000..f9fd17c6 --- /dev/null +++ b/packages/react-dataparcels/Action.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('dataparcels/Action.js'); diff --git a/packages/react-dataparcels/CancelActionMarker.js b/packages/react-dataparcels/CancelActionMarker.js new file mode 100644 index 00000000..1fb6c363 --- /dev/null +++ b/packages/react-dataparcels/CancelActionMarker.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('dataparcels/CancelActionMarker.js'); diff --git a/packages/react-dataparcels/ChangeRequest.js b/packages/react-dataparcels/ChangeRequest.js new file mode 100644 index 00000000..d6b3b673 --- /dev/null +++ b/packages/react-dataparcels/ChangeRequest.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('dataparcels/ChangeRequest.js'); diff --git a/packages/react-dataparcels/DeletedParcelMarker.js b/packages/react-dataparcels/DeletedParcelMarker.js new file mode 100644 index 00000000..81925835 --- /dev/null +++ b/packages/react-dataparcels/DeletedParcelMarker.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('dataparcels/DeletedParcelMarker.js'); diff --git a/packages/react-dataparcels/ParcelBoundary.js b/packages/react-dataparcels/ParcelBoundary.js new file mode 100644 index 00000000..3f22ef33 --- /dev/null +++ b/packages/react-dataparcels/ParcelBoundary.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('./lib/ParcelBoundary.js'); diff --git a/packages/react-dataparcels/ParcelBoundaryHoc.js b/packages/react-dataparcels/ParcelBoundaryHoc.js new file mode 100644 index 00000000..a2ff3bc4 --- /dev/null +++ b/packages/react-dataparcels/ParcelBoundaryHoc.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('./lib/ParcelBoundaryHoc.js'); diff --git a/packages/react-dataparcels/ParcelHoc.js b/packages/react-dataparcels/ParcelHoc.js new file mode 100644 index 00000000..a0bd9b70 --- /dev/null +++ b/packages/react-dataparcels/ParcelHoc.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('./lib/ParcelHoc.js'); diff --git a/packages/react-dataparcels/ParcelShape.js b/packages/react-dataparcels/ParcelShape.js new file mode 100644 index 00000000..1de2afa9 --- /dev/null +++ b/packages/react-dataparcels/ParcelShape.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('dataparcels/ParcelShape.js'); diff --git a/packages/react-dataparcels/__test__/Exports-test.js b/packages/react-dataparcels/__test__/Exports-test.js new file mode 100644 index 00000000..d5300435 --- /dev/null +++ b/packages/react-dataparcels/__test__/Exports-test.js @@ -0,0 +1,69 @@ +// @flow + +// dataparcels exports +import Parcel from '../src/index'; +import Action from '../Action'; +import ChangeRequest from '../ChangeRequest'; +import DeletedParcelMarker from '../DeletedParcelMarker'; +import ParcelShape from '../ParcelShape'; +import shape from '../shape'; +import CancelActionMarker from '../CancelActionMarker'; + +// react-dataparcels +import ParcelHoc from '../ParcelHoc'; +import ParcelBoundary from '../ParcelBoundary'; +import ParcelBoundaryHoc from '../ParcelBoundaryHoc'; + +// internal dataparcels files +import InternalParcel from 'dataparcels'; +import InternalAction from 'dataparcels/Action'; +import InternalChangeRequest from 'dataparcels/ChangeRequest'; +import InternalDeletedParcelMarker from 'dataparcels/DeletedParcelMarker'; +import InternalParcelShape from 'dataparcels/ParcelShape'; +import InternalShape from 'dataparcels/shape'; +import InternalCancelActionMarker from 'dataparcels/CancelActionMarker'; + +// internal react-dataparcels +import InternalParcelHoc from '../lib/ParcelHoc'; +import InternalParcelBoundary from '../lib/ParcelBoundary'; +import InternalParcelBoundaryHoc from '../lib/ParcelBoundaryHoc'; + +test('index should export Parcel', () => { + expect(Parcel).toBe(InternalParcel); +}); + +test('/Action should export Action', () => { + expect(Action).toBe(InternalAction); +}); + +test('/ChangeRequest should export ChangeRequest', () => { + expect(ChangeRequest).toBe(InternalChangeRequest); +}); + +test('/DeletedParcelMarker should export DeletedParcelMarker', () => { + expect(DeletedParcelMarker).toBe(InternalDeletedParcelMarker); +}); + +test('/ParcelShape should export ParcelShape', () => { + expect(ParcelShape).toBe(InternalParcelShape); +}); + +test('/shape should export shape', () => { + expect(shape).toBe(InternalShape); +}); + +test('/CancelActionMarker should export CancelActionMarker', () => { + expect(CancelActionMarker).toBe(InternalCancelActionMarker); +}); + +test('/ParcelHoc should export ParcelHoc', () => { + expect(ParcelHoc).toBe(InternalParcelHoc); +}); + +test('/ParcelBoundary should export ParcelBoundary', () => { + expect(ParcelBoundary).toBe(InternalParcelBoundary); +}); + +test('/ParcelBoundaryHoc should export ParcelBoundaryHoc', () => { + expect(ParcelBoundaryHoc).toBe(InternalParcelBoundaryHoc); +}); diff --git a/packages/react-dataparcels/package.json b/packages/react-dataparcels/package.json index d973d620..969878f8 100644 --- a/packages/react-dataparcels/package.json +++ b/packages/react-dataparcels/package.json @@ -1,6 +1,6 @@ { "name": "react-dataparcels", - "version": "0.17.2", + "version": "0.18.0-2", "description": "A library for editing data structures that works really well with React.", "main": "lib/index.js", "license": "UNLICENSED", @@ -17,13 +17,16 @@ }, "private": false, "scripts": { - "build": "rm -rf lib && NODE_ENV=production babel src --out-dir lib --ignore **/*-test.js", + "build": "rm -rf lib && NODE_ENV=production babel src --out-dir lib --ignore '**/__test__/*.js'", "build-all": "yarn build", "watch": "yarn run build -w" }, "dependencies": { "babel-runtime": "6.23.0", - "dataparcels": "^0.17.2", - "unmutable": "^0.39.0" + "dataparcels": "^0.18.0-2", + "unmutable": "^0.41.1" + }, + "peerDependencies": { + "react": "16.4.2" } } diff --git a/packages/react-dataparcels/shape.js b/packages/react-dataparcels/shape.js new file mode 100644 index 00000000..65829152 --- /dev/null +++ b/packages/react-dataparcels/shape.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +module.exports = require('dataparcels/shape.js'); diff --git a/packages/react-dataparcels/src/ParcelBoundary.jsx b/packages/react-dataparcels/src/ParcelBoundary.jsx index 173140d2..4575635f 100644 --- a/packages/react-dataparcels/src/ParcelBoundary.jsx +++ b/packages/react-dataparcels/src/ParcelBoundary.jsx @@ -1,8 +1,9 @@ // @flow -import React from 'react'; import type {Node} from 'react'; +import type ChangeRequest from 'dataparcels/ChangeRequest'; + +import React from 'react'; import Parcel from 'dataparcels'; -import type {ChangeRequest} from 'dataparcels'; import ParcelBoundaryEquals from './util/ParcelBoundaryEquals'; import shallowEquals from 'unmutable/lib/shallowEquals'; @@ -26,7 +27,8 @@ type Props = { hold: boolean, forceUpdate: Array<*>, parcel: Parcel, - pure: boolean + pure: boolean, + keepState: boolean }; type State = { @@ -45,7 +47,8 @@ export default class ParcelBoundary extends React.Component { /* e debugParcel: false, hold: false, forceUpdate: [], - pure: true + pure: true, + keepState: false }; constructor(props: Props) { @@ -74,7 +77,7 @@ export default class ParcelBoundary extends React.Component { /* e let parcelDataChanged: boolean = !ParcelBoundaryEquals(this.props.parcel, nextProps.parcel); - if(!parcelDataChanged && (nextProps.debounce || nextProps.hold)) { + if(!parcelDataChanged) { parcelDataChanged = !ParcelBoundaryEquals(this.state.parcel, nextState.parcel); } @@ -85,13 +88,24 @@ export default class ParcelBoundary extends React.Component { /* e } static getDerivedStateFromProps(props: Props, state: State): * { - let {parcel} = props; + let { + parcel, + keepState + } = props; + let { makeBoundarySplit, parcelFromProps } = state; - if(parcel !== parcelFromProps) { + let updateState = parcel !== parcelFromProps; + + if(keepState && parcel._lastOriginId.startsWith(parcel.id)) { + // if keepState, don't update state if the last change came from within this parcel boundary + updateState = false; + } + + if(updateState) { var newState: any = { parcelFromProps: parcel }; @@ -113,15 +127,6 @@ export default class ParcelBoundary extends React.Component { /* e return null; } - debugRenderStyle: Function = (): Object => { - let rand = () => Math.floor((Math.random() * 0.75 + 0.25) * 256); - return { - backgroundColor: `rgb(${rand()},${rand()},${rand()})`, - padding: "1rem", - marginBottom: "1rem" - }; - }; - addToBuffer: Function = (changeRequest: ChangeRequest) => (state: State): State => { let {debugBuffer} = this.props; let { @@ -212,13 +217,11 @@ export default class ParcelBoundary extends React.Component { /* e newParcel.toConsole(); } - let shouldBeSynchronous = () => changeRequest.shouldBeSynchronous(); - let updateParcel = set('parcel', newParcel); let addToBuffer = this.addToBuffer(changeRequest); let releaseBuffer = this.releaseBuffer(); - if((!debounce && !hold) || shouldBeSynchronous()) { + if(!debounce && !hold) { this.setState(pipe( updateParcel, addToBuffer, @@ -262,13 +265,8 @@ export default class ParcelBoundary extends React.Component { /* e cancel: () => this.setState(this.cancelBuffer()), release: () => this.setState(this.releaseBuffer()) }; - let buffered = changeCount > 0; - - let element = children(parcel, actions, buffered); - if(parcel._treeshare.debugRender) { - return
{element}
; - } - return element; + let buffered = changeCount > 0; + return children(parcel, actions, buffered); } } diff --git a/packages/react-dataparcels/src/ParcelHoc.jsx b/packages/react-dataparcels/src/ParcelHoc.jsx index e4f2a866..4aa16b83 100644 --- a/packages/react-dataparcels/src/ParcelHoc.jsx +++ b/packages/react-dataparcels/src/ParcelHoc.jsx @@ -1,7 +1,7 @@ // @flow import type {ComponentType} from 'react'; import type {Node} from 'react'; -import type {ChangeRequest} from 'dataparcels'; +import type ChangeRequest from 'dataparcels/ChangeRequest'; import React from 'react'; import Parcel from 'dataparcels'; @@ -34,8 +34,7 @@ type ParcelHocConfig = { onChange?: (props: AnyProps) => OnChange, delayUntil?: (props: AnyProps) => boolean, pipe?: (props: *) => (parcel: Parcel) => Parcel, - debugParcel?: boolean, - debugRender?: boolean + debugParcel?: boolean }; const PARCEL_HOC_NAME = `ParcelHoc()`; @@ -51,8 +50,7 @@ export default (config: ParcelHocConfig): Function => { delayUntil = (props) => true, /* eslint-disable-line no-unused-vars */ pipe = props => ii => ii, /* eslint-disable-line no-unused-vars */ // debug options - debugParcel = false, - debugRender = false + debugParcel = false } = config; Types(PARCEL_HOC_NAME, "config.name", "string")(name); @@ -63,7 +61,6 @@ export default (config: ParcelHocConfig): Function => { Types(PARCEL_HOC_NAME, "config.delayUntil", "function")(delayUntil); Types(PARCEL_HOC_NAME, "config.pipe", "function")(pipe); Types(PARCEL_HOC_NAME, "config.debugParcel", "boolean")(debugParcel); - Types(PARCEL_HOC_NAME, "config.debugRender", "boolean")(debugRender); return (Component: ComponentType) => class ParcelHoc extends React.Component { constructor(props: Props) { @@ -71,8 +68,7 @@ export default (config: ParcelHocConfig): Function => { let initialize = (value: *) => new Parcel({ value, - handleChange: this.handleChange, - debugRender + handleChange: this.handleChange }); this.state = { @@ -98,10 +94,7 @@ export default (config: ParcelHocConfig): Function => { if(parcel && shouldParcelUpdateFromProps && shouldParcelUpdateFromProps(state.prevProps, props, valueFromProps)) { // $FlowFixMe - parcel cant possibly be undefined here - newState.parcel = parcel.batchAndReturn((parcel: Parcel) => { - // $FlowFixMe - newValueFromProps cant possibly be undefined here - parcel.set(valueFromProps(props)); - }); + newState.parcel = parcel._setAndReturn(valueFromProps(props)); if(debugParcel) { log(`Parcel updated from props:`); diff --git a/packages/react-dataparcels/src/__test__/ParcelBoundary-test.js b/packages/react-dataparcels/src/__test__/ParcelBoundary-test.js index d91b516f..d7fb6820 100644 --- a/packages/react-dataparcels/src/__test__/ParcelBoundary-test.js +++ b/packages/react-dataparcels/src/__test__/ParcelBoundary-test.js @@ -282,29 +282,6 @@ test('ParcelBoundary should cancel unreleased changes when receiving a new parce expect(handleChange).toHaveBeenCalledTimes(0); }); -test('ParcelBoundary should ignore debounce when sending a ping', () => { - let childRenderer = jest.fn(); - let handleChange = jest.fn(); - - let parcel = new Parcel({ - value: 123, - handleChange - }); - - let wrapper = shallow( - {childRenderer} - ); - - let childParcel = childRenderer.mock.calls[0][0]; - childParcel.ping(); - - // even with debounce applied, handleChange should have been called immediately - expect(handleChange).toHaveBeenCalledTimes(1); - - // handleChange should have the same value as before - expect(handleChange.mock.calls[0][0].value).toBe(123); -}); - test('ParcelBoundary should use an internal boundary split to stop parcel boundaries using the same parcel from sharing their parcel registries', () => { let parcel = new Parcel({ value: { @@ -339,19 +316,45 @@ test('ParcelBoundary should use an internal boundary split to stop parcel bounda expect(childParcelB2.value).toEqual({abc: 123, def: 456}); }); -test('ParcelBoundary should render colours when debugRender is true', () => { - let hasChanged = false; +test('ParcelBoundary should ignore updates from props for updates caused by themselves if keepState is true', () => { + let childRenderer = jest.fn(); + let handleChange = jest.fn(); + let parcel = new Parcel({ value: 123, - debugRender: true + handleChange + }); + + let withModify = (parcel) => parcel.modifyUp(value => value + 1); + + let wrapper = shallow( + {childRenderer} + ); + + let childParcel = childRenderer.mock.calls[0][0]; + childParcel.onChange(456); + + let newParcel = handleChange.mock.calls[0][0]; + + // verify that the current value of the parcel has been updated + expect(newParcel.value).toBe(457); + + wrapper.setProps({ + parcel: withModify(newParcel) + }); + + // expect that the value in the parcelboundary has not changed + // because the last change was triggered by this boundary + let childParcel2 = childRenderer.mock.calls[2][0]; + expect(childParcel2.value).toBe(456); + + // make a change externally and ensure that the value in the boundary does update + newParcel.set(789); + let newParcel2 = handleChange.mock.calls[1][0]; + wrapper.setProps({ + parcel: withModify(newParcel2) }); - expect( - shallow({(pp) => "???"}) - .render() - .get(0) - .attribs - .style - .indexOf('background-color') !== -1 - ).toBe(true); + let childParcel3 = childRenderer.mock.calls[3][0]; + expect(childParcel3.value).toBe(789); }); diff --git a/packages/react-dataparcels/src/__test__/ParcelHoc-test.js b/packages/react-dataparcels/src/__test__/ParcelHoc-test.js index c8ebae51..87fce0ce 100644 --- a/packages/react-dataparcels/src/__test__/ParcelHoc-test.js +++ b/packages/react-dataparcels/src/__test__/ParcelHoc-test.js @@ -99,7 +99,7 @@ test('ParcelHoc config should accept a pipe function', () => { pipe: (props) => (parcel) => { expect(456).toBe(parcel.value); expect({}).toEqual(props); - return parcel.modifyValue(ii => ii + 1); + return parcel.modifyDown(ii => ii + 1); } }) ).props(); @@ -107,19 +107,6 @@ test('ParcelHoc config should accept a pipe function', () => { expect(457).toBe(childProps.proppy.value); }); -test('ParcelHoc config should accept a debugRender boolean', () => { - let childProps = shallowRenderHoc( - {}, - ParcelHoc({ - valueFromProps: () => 456, - name: "proppy", - debugRender: true - }) - ).props(); - - expect(childProps.proppy._treeshare.debugRender).toBe(true); -}); - test('ParcelHoc config should accept a debugParcel boolean', () => { let {log} = console; // $FlowFixMe diff --git a/packages/react-dataparcels/src/index.js b/packages/react-dataparcels/src/index.js index 60a0cb17..c1583bf8 100644 --- a/packages/react-dataparcels/src/index.js +++ b/packages/react-dataparcels/src/index.js @@ -1,28 +1,32 @@ // @flow + +// +// dataparcels +// + import dataparcels from 'dataparcels'; export default dataparcels; -export {Action} from 'dataparcels'; -export {ActionCreators} from 'dataparcels'; -export {ChangeRequest} from 'dataparcels'; -export {DeletedParcelMarker} from 'dataparcels'; +// +// dataparcels types +// export type {ParcelData} from 'dataparcels'; export type {ParcelConfig} from 'dataparcels'; export type {ParcelConfigInternal} from 'dataparcels'; +export type {ParcelCreateConfigType} from 'dataparcels'; export type {ParcelMeta} from 'dataparcels'; -export type {ParcelMetaUpdater} from 'dataparcels'; -export type {ParcelBatcher} from 'dataparcels'; export type {ParcelMapper} from 'dataparcels'; export type {ParcelUpdater} from 'dataparcels'; export type {ParcelValueUpdater} from 'dataparcels'; +export type {ParcelShapeUpdater} from 'dataparcels'; + +export type {ParcelShapeSetMeta} from 'dataparcels'; +export type {ParcelShapeValueUpdater} from 'dataparcels'; +export type {ParcelShapeConfigInternal} from 'dataparcels'; export type {Key} from 'dataparcels'; export type {Index} from 'dataparcels'; export type {Property} from 'dataparcels'; - - -export {default as ParcelHoc} from './ParcelHoc'; -export {default as ParcelBoundary} from './ParcelBoundary'; -export {default as ParcelBoundaryHoc} from './ParcelBoundaryHoc'; +export type {ParentType} from 'dataparcels'; diff --git a/yarn.lock b/yarn.lock index ed315301..c3ddf6f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -96,6 +96,12 @@ esutils "^2.0.2" js-tokens "^3.0.0" +"@babel/runtime@^7.1.5", "@babel/runtime@^7.2.0": + version "7.3.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.1.tgz#574b03e8e8a9898eaf4a872a92ea20b7846f6f2a" + dependencies: + regenerator-runtime "^0.12.0" + "@babel/template@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f" @@ -143,6 +149,17 @@ pretty-ms "^0.2.1" text-table "^0.2.0" +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + "@sinonjs/formatio@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-2.0.0.tgz#84db7e9eb5531df18a8c5e0bfb6e449e55e654b2" @@ -197,6 +214,10 @@ version "7.0.68" resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.68.tgz#f468d9a4407259faa64b472f64d795ff85d51a77" +"@types/q@^1.5.1": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18" + "@types/react-router-dom@^4.2.2": version "4.3.0" resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.0.tgz#c91796d02deb3a5b24bc1c5db4a255df0d18b8b5" @@ -222,6 +243,139 @@ version "0.0.32" resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.32.tgz#0d3cb31022f8427ea58c008af32b80da126ca4e3" +"@webassemblyjs/ast@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace" + dependencies: + "@webassemblyjs/helper-module-context" "1.7.11" + "@webassemblyjs/helper-wasm-bytecode" "1.7.11" + "@webassemblyjs/wast-parser" "1.7.11" + +"@webassemblyjs/floating-point-hex-parser@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz#a69f0af6502eb9a3c045555b1a6129d3d3f2e313" + +"@webassemblyjs/helper-api-error@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz#c7b6bb8105f84039511a2b39ce494f193818a32a" + +"@webassemblyjs/helper-buffer@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz#3122d48dcc6c9456ed982debe16c8f37101df39b" + +"@webassemblyjs/helper-code-frame@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz#cf8f106e746662a0da29bdef635fcd3d1248364b" + dependencies: + "@webassemblyjs/wast-printer" "1.7.11" + +"@webassemblyjs/helper-fsm@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz#df38882a624080d03f7503f93e3f17ac5ac01181" + +"@webassemblyjs/helper-module-context@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz#d874d722e51e62ac202476935d649c802fa0e209" + +"@webassemblyjs/helper-wasm-bytecode@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz#dd9a1e817f1c2eb105b4cf1013093cb9f3c9cb06" + +"@webassemblyjs/helper-wasm-section@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz#9c9ac41ecf9fbcfffc96f6d2675e2de33811e68a" + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/helper-buffer" "1.7.11" + "@webassemblyjs/helper-wasm-bytecode" "1.7.11" + "@webassemblyjs/wasm-gen" "1.7.11" + +"@webassemblyjs/ieee754@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz#c95839eb63757a31880aaec7b6512d4191ac640b" + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.7.11.tgz#d7267a1ee9c4594fd3f7e37298818ec65687db63" + dependencies: + "@xtuc/long" "4.2.1" + +"@webassemblyjs/utf8@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.7.11.tgz#06d7218ea9fdc94a6793aa92208160db3d26ee82" + +"@webassemblyjs/wasm-edit@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz#8c74ca474d4f951d01dbae9bd70814ee22a82005" + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/helper-buffer" "1.7.11" + "@webassemblyjs/helper-wasm-bytecode" "1.7.11" + "@webassemblyjs/helper-wasm-section" "1.7.11" + "@webassemblyjs/wasm-gen" "1.7.11" + "@webassemblyjs/wasm-opt" "1.7.11" + "@webassemblyjs/wasm-parser" "1.7.11" + "@webassemblyjs/wast-printer" "1.7.11" + +"@webassemblyjs/wasm-gen@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz#9bbba942f22375686a6fb759afcd7ac9c45da1a8" + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/helper-wasm-bytecode" "1.7.11" + "@webassemblyjs/ieee754" "1.7.11" + "@webassemblyjs/leb128" "1.7.11" + "@webassemblyjs/utf8" "1.7.11" + +"@webassemblyjs/wasm-opt@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz#b331e8e7cef8f8e2f007d42c3a36a0580a7d6ca7" + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/helper-buffer" "1.7.11" + "@webassemblyjs/wasm-gen" "1.7.11" + "@webassemblyjs/wasm-parser" "1.7.11" + +"@webassemblyjs/wasm-parser@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz#6e3d20fa6a3519f6b084ef9391ad58211efb0a1a" + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/helper-api-error" "1.7.11" + "@webassemblyjs/helper-wasm-bytecode" "1.7.11" + "@webassemblyjs/ieee754" "1.7.11" + "@webassemblyjs/leb128" "1.7.11" + "@webassemblyjs/utf8" "1.7.11" + +"@webassemblyjs/wast-parser@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz#25bd117562ca8c002720ff8116ef9072d9ca869c" + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/floating-point-hex-parser" "1.7.11" + "@webassemblyjs/helper-api-error" "1.7.11" + "@webassemblyjs/helper-code-frame" "1.7.11" + "@webassemblyjs/helper-fsm" "1.7.11" + "@xtuc/long" "4.2.1" + +"@webassemblyjs/wast-printer@1.7.11": + version "1.7.11" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz#c4245b6de242cb50a2cc950174fdbf65c78d7813" + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/wast-parser" "1.7.11" + "@xtuc/long" "4.2.1" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + +"@xtuc/long@4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.1.tgz#5c85d662f76fa1d34575766c5dcd6615abcd30d8" + "@zeit/schemas@1.7.0": version "1.7.0" resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-1.7.0.tgz#84624e270a0d420714be7279ffabc7e1745afc1a" @@ -252,6 +406,12 @@ accepts@^1.3.0, accepts@~1.3.4, accepts@~1.3.5: mime-types "~2.1.18" negotiator "0.6.1" +acorn-dynamic-import@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz#901ceee4c7faaef7e07ad2a47e890675da50a278" + dependencies: + acorn "^5.0.0" + acorn-globals@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.1.0.tgz#ab716025dbe17c54d3ef81d32ece2b2d99fe2538" @@ -278,6 +438,10 @@ acorn@^5.0.0, acorn@^5.0.3, acorn@^5.5.0, acorn@^5.5.3, acorn@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" +acorn@^5.6.2, acorn@^5.7.3: + version "5.7.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" + add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" @@ -290,11 +454,15 @@ after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + ajv-keywords@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" -ajv-keywords@^3.0.0: +ajv-keywords@^3.0.0, ajv-keywords@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" @@ -323,6 +491,15 @@ ajv@^5.0.0, ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" +ajv@^6.1.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.7.0.tgz#e3ce7bb372d6577bb1839f1dfdfcbf5ad2948d96" + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -331,7 +508,7 @@ align-text@^0.1.1, align-text@^0.1.3: longest "^1.0.1" repeat-string "^1.5.2" -alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: +alphanum-sort@^1.0.0, alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -413,7 +590,7 @@ append-transform@^1.0.0: dependencies: default-require-extensions "^2.0.0" -aproba@^1.0.3: +aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -607,12 +784,6 @@ async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" -async@2.6.1, async@^2.0.1, async@^2.1.2, async@^2.1.4: - version "2.6.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" - dependencies: - lodash "^4.17.10" - async@^0.9.0: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" @@ -621,6 +792,12 @@ async@^1.3.0, async@^1.4.0, async@^1.5.0: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" +async@^2.0.1, async@^2.1.2, async@^2.1.4: + version "2.6.1" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" + dependencies: + lodash "^4.17.10" + async@~0.2.6: version "0.2.10" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" @@ -1781,10 +1958,23 @@ better-queue@^3.8.6, better-queue@^3.8.7: node-eta "^0.9.0" uuid "^3.0.0" +bfj@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.1.tgz#05a3b7784fbd72cfa3c22e56002ef99336516c48" + dependencies: + bluebird "^3.5.1" + check-types "^7.3.0" + hoopy "^0.1.2" + tryer "^1.0.0" + big.js@^3.1.3: version "3.2.0" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + binary-extensions@^1.0.0: version "1.11.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" @@ -1810,6 +2000,10 @@ bluebird@^3.0.0, bluebird@^3.0.5, bluebird@^3.3.4, bluebird@^3.5.0: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" +bluebird@^3.5.1, bluebird@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" + blueflag-test@^0.18.1: version "0.18.1" resolved "https://registry.yarnpkg.com/blueflag-test/-/blueflag-test-0.18.1.tgz#2f1ef068b6a270f9152e872a52cab61b740162bd" @@ -1882,7 +2076,22 @@ body-parser@1.18.2: raw-body "2.3.2" type-is "~1.6.15" -boolbase@~1.0.0: +body-parser@1.18.3: + version "1.18.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "~1.6.3" + iconv-lite "0.4.23" + on-finished "~2.3.0" + qs "6.5.2" + raw-body "2.3.3" + type-is "~1.6.16" + +boolbase@^1.0.0, boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -1919,7 +2128,7 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" -braces@^2.3.1: +braces@^2.3.0, braces@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" dependencies: @@ -2027,6 +2236,14 @@ browserslist@^3.2.6: caniuse-lite "^1.0.30000844" electron-to-chromium "^1.3.47" +browserslist@^4.0.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.4.0.tgz#7050d1412cbfc5274aba609ed5e50359ca1a5fdf" + dependencies: + caniuse-lite "^1.0.30000928" + electron-to-chromium "^1.3.100" + node-releases "^1.1.3" + bruce@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/bruce/-/bruce-4.0.0.tgz#e6f05dde87e31d849744c68c54727482164c822b" @@ -2090,10 +2307,29 @@ byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" -bytes@3.0.0: +bytes@3.0.0, bytes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" +cacache@^11.0.2, cacache@^11.2.0: + version "11.3.2" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.2.tgz#2d81e308e3d258ca38125b676b98b2ac9ce69bfa" + dependencies: + bluebird "^3.5.3" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.3" + graceful-fs "^4.1.15" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.2" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -2133,12 +2369,24 @@ call-signature@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/call-signature/-/call-signature-0.0.2.tgz#a84abc825a55ef4cb2b028bd74e205a65b9a4996" +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + dependencies: + callsites "^2.0.0" + caller-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" dependencies: callsites "^0.2.0" +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + dependencies: + caller-callsite "^2.0.0" + callsite@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" @@ -2182,6 +2430,10 @@ camelcase@^4.0.0, camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" +camelcase@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" + caniuse-api@^1.5.2, caniuse-api@^1.5.3: version "1.6.1" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" @@ -2191,10 +2443,23 @@ caniuse-api@^1.5.2, caniuse-api@^1.5.3: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000872" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000872.tgz#3f6e53b63d373768bf99e896133d66ef89c49999" +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000928: + version "1.0.30000928" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000928.tgz#805e828dc72b06498e3683a32e61c7507fd67b88" + caniuse-lite@^1.0.30000844: version "1.0.30000872" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000872.tgz#aa1346ac88ed8dcdc7e372a42989bf9bea696f06" @@ -2250,6 +2515,14 @@ chalk@^0.4.0: has-color "~0.1.0" strip-ansi "~0.1.0" +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + character-entities-html4@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.2.tgz#c44fdde3ce66b52e8d321d6c1bf46101f0150610" @@ -2274,6 +2547,10 @@ charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" +check-types@^7.3.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.4.0.tgz#0378ec1b9616ec71f774931a3c6516fad8c152f4" + cheerio@^0.22.0: version "0.22.0" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" @@ -2321,10 +2598,39 @@ chokidar@^1.0.0, chokidar@^1.4.2, chokidar@^1.6.1, chokidar@^1.7.0: optionalDependencies: fsevents "^1.0.0" +chokidar@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" + dependencies: + anymatch "^2.0.0" + async-each "^1.0.0" + braces "^2.3.0" + glob-parent "^3.1.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + lodash.debounce "^4.0.8" + normalize-path "^2.1.1" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + upath "^1.0.5" + optionalDependencies: + fsevents "^1.2.2" + chownr@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" +chownr@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" + +chrome-trace-event@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48" + dependencies: + tslib "^1.9.0" + chunk-manifest-webpack-plugin@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/chunk-manifest-webpack-plugin/-/chunk-manifest-webpack-plugin-0.1.0.tgz#6138488fc21ddab4ccfb7c1c11d51bb80a943186" @@ -2335,6 +2641,10 @@ ci-info@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.3.tgz#710193264bb05c77b8c90d02f5aaf22216a667b2" +ci-job-number@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/ci-job-number/-/ci-job-number-0.3.0.tgz#34bdd114b0dece1960287bd40a57051041a2a800" + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -2478,6 +2788,14 @@ coa@~1.0.1: dependencies: q "^1.1.2" +coa@~2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + code-excerpt@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/code-excerpt/-/code-excerpt-2.1.1.tgz#5fe3057bfbb71a5f300f659ef2cc0a47651ba77c" @@ -2509,11 +2827,17 @@ color-convert@^1.3.0, color-convert@^1.9.0: dependencies: color-name "1.1.1" +color-convert@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + dependencies: + color-name "1.1.3" + color-name@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" -color-name@^1.0.0: +color-name@1.1.3, color-name@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" @@ -2523,6 +2847,13 @@ color-string@^0.3.0: dependencies: color-name "^1.0.0" +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + color@^0.10.1: version "0.10.1" resolved "https://registry.yarnpkg.com/color/-/color-0.10.1.tgz#c04188df82a209ddebccecdacd3ec320f193739f" @@ -2538,6 +2869,13 @@ color@^0.11.0, color@^0.11.3, color@^0.11.4: color-convert "^1.3.0" color-string "^0.3.0" +color@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.0.tgz#d8e9fb096732875774c84bf922815df0308d0ffc" + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + colormin@^1.0.5: version "1.1.2" resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" @@ -2589,10 +2927,6 @@ command-join@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/command-join/-/command-join-2.0.0.tgz#52e8b984f4872d952ff1bdc8b98397d27c7144cf" -commander@2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" - commander@2.9.0, commander@2.9.x: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" @@ -2603,6 +2937,14 @@ commander@^2.11.0, commander@^2.9.0: version "2.16.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.16.0.tgz#f16390593996ceb4f3eeb020b31d78528f7f8a50" +commander@^2.18.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + +commander@~2.17.1: + version "2.17.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + common-path-prefix@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-1.0.0.tgz#cd52f6f0712e0baab97d6f9732874f22f47752c0" @@ -2654,6 +2996,17 @@ compressible@~2.0.14: dependencies: mime-db ">= 1.34.0 < 2" +compression-webpack-plugin@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-2.0.0.tgz#46476350c1eb27f783dccc79ac2f709baa2cffbc" + dependencies: + cacache "^11.2.0" + find-cache-dir "^2.0.0" + neo-async "^2.5.0" + schema-utils "^1.0.0" + serialize-javascript "^1.4.0" + webpack-sources "^1.0.1" + compression@^1.5.2: version "1.7.3" resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.3.tgz#27e0e176aaf260f7f2c2813c3e440adb9f1993db" @@ -2670,7 +3023,7 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.4.10, concat-stream@^1.6.0: +concat-stream@^1.4.10, concat-stream@^1.5.0, concat-stream@^1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" dependencies: @@ -2902,6 +3255,17 @@ cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -2936,6 +3300,15 @@ core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" +cosmiconfig@^5.0.0, cosmiconfig@^5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.7.tgz#39826b292ee0d78eda137dfa3173bd1c21a43b04" + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.9.0" + parse-json "^4.0.0" + create-ecdh@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" @@ -3007,7 +3380,7 @@ cross-spawn@^4: lru-cache "^4.0.1" which "^1.2.9" -cross-spawn@^6.0.5: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" dependencies: @@ -3065,10 +3438,17 @@ css-color-function@^1.2.0: debug "^3.1.0" rgb "~0.1.0" -css-color-names@0.0.4, css-color-names@~0.0.3: +css-color-names@0.0.4, css-color-names@^0.0.4, css-color-names@~0.0.3: version "0.0.4" resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" +css-declaration-sorter@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" + dependencies: + postcss "^7.0.1" + timsort "^0.3.0" + css-loader@^0.26.1: version "0.26.4" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.26.4.tgz#b61e9e30db94303e6ffc892f10ecd09ad025a1fd" @@ -3086,6 +3466,25 @@ css-loader@^0.26.1: postcss-modules-values "^1.1.0" source-list-map "^0.1.7" +css-loader@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.0.tgz#42952ac22bca5d076978638e9813abce49b8f0cc" + dependencies: + icss-utils "^4.0.0" + loader-utils "^1.2.1" + lodash "^4.17.11" + postcss "^7.0.6" + postcss-modules-extract-imports "^2.0.0" + postcss-modules-local-by-default "^2.0.3" + postcss-modules-scope "^2.0.0" + postcss-modules-values "^2.0.0" + postcss-value-parser "^3.3.0" + schema-utils "^1.0.0" + +css-select-base-adapter@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + css-select@^1.1.0, css-select@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" @@ -3095,6 +3494,15 @@ css-select@^1.1.0, css-select@~1.2.0: domutils "1.5.1" nth-check "~1.0.1" +css-select@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.0.2.tgz#ab4386cec9e1f668855564b17c3733b43b2a5ede" + dependencies: + boolbase "^1.0.0" + css-what "^2.1.2" + domutils "^1.7.0" + nth-check "^1.0.2" + css-selector-parser@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.3.0.tgz#5f1ad43e2d8eefbfdc304fcd39a521664943e3eb" @@ -3107,14 +3515,97 @@ css-selector-tokenizer@^0.7.0: fastparse "^1.1.1" regexpu-core "^1.0.0" +css-tree@1.0.0-alpha.28: + version "1.0.0-alpha.28" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.28.tgz#8e8968190d886c9477bc8d61e96f61af3f7ffa7f" + dependencies: + mdn-data "~1.1.0" + source-map "^0.5.3" + +css-tree@1.0.0-alpha.29: + version "1.0.0-alpha.29" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.29.tgz#3fa9d4ef3142cbd1c301e7664c1f352bd82f5a39" + dependencies: + mdn-data "~1.1.0" + source-map "^0.5.3" + +css-unit-converter@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" + +css-url-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/css-url-regex/-/css-url-regex-1.1.0.tgz#83834230cc9f74c457de59eebd1543feeb83b7ec" + css-what@2.1: version "2.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" +css-what@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.2.tgz#c0876d9d0480927d7d4920dcd72af3595649554d" + cssesc@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" +cssesc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" + +cssnano-preset-default@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.6.tgz#92379e2a6db4a91c0ea727f5f556eeac693eab6a" + dependencies: + css-declaration-sorter "^4.0.1" + cssnano-util-raw-cache "^4.0.1" + postcss "^7.0.0" + postcss-calc "^7.0.0" + postcss-colormin "^4.0.2" + postcss-convert-values "^4.0.1" + postcss-discard-comments "^4.0.1" + postcss-discard-duplicates "^4.0.2" + postcss-discard-empty "^4.0.1" + postcss-discard-overridden "^4.0.1" + postcss-merge-longhand "^4.0.10" + postcss-merge-rules "^4.0.2" + postcss-minify-font-values "^4.0.2" + postcss-minify-gradients "^4.0.1" + postcss-minify-params "^4.0.1" + postcss-minify-selectors "^4.0.1" + postcss-normalize-charset "^4.0.1" + postcss-normalize-display-values "^4.0.1" + postcss-normalize-positions "^4.0.1" + postcss-normalize-repeat-style "^4.0.1" + postcss-normalize-string "^4.0.1" + postcss-normalize-timing-functions "^4.0.1" + postcss-normalize-unicode "^4.0.1" + postcss-normalize-url "^4.0.1" + postcss-normalize-whitespace "^4.0.1" + postcss-ordered-values "^4.1.1" + postcss-reduce-initial "^4.0.2" + postcss-reduce-transforms "^4.0.1" + postcss-svgo "^4.0.1" + postcss-unique-selectors "^4.0.1" + +cssnano-util-get-arguments@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" + +cssnano-util-get-match@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" + +cssnano-util-raw-cache@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" + dependencies: + postcss "^7.0.0" + +cssnano-util-same-parent@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" + "cssnano@>=2.6.1 <4": version "3.10.0" resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" @@ -3152,6 +3643,21 @@ cssesc@^0.1.0: postcss-value-parser "^3.2.3" postcss-zindex "^2.0.1" +cssnano@^4.1.0: + version "4.1.8" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.8.tgz#8014989679d5fd42491e4499a521dbfb85c95fd1" + dependencies: + cosmiconfig "^5.0.0" + cssnano-preset-default "^4.0.6" + is-resolvable "^1.0.0" + postcss "^7.0.0" + +csso@^3.5.0: + version "3.5.1" + resolved "https://registry.yarnpkg.com/csso/-/csso-3.5.1.tgz#7b9eb8be61628973c1b261e169d2f024008e758b" + dependencies: + css-tree "1.0.0-alpha.29" + csso@~2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85" @@ -3179,6 +3685,10 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" +cyclist@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" + d@1: version "1.0.0" resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" @@ -3264,7 +3774,7 @@ decamelize-keys@^1.0.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2: +decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -3483,6 +3993,13 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +dir-glob@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" + dependencies: + arrify "^1.0.1" + path-type "^3.0.0" + discontinuous-range@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" @@ -3561,7 +4078,7 @@ domutils@1.5.1: dom-serializer "0" domelementtype "1" -domutils@^1.5.1: +domutils@^1.5.1, domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" dependencies: @@ -3574,7 +4091,7 @@ dot-prop@^3.0.0: dependencies: is-obj "^1.0.0" -dot-prop@^4.1.0: +dot-prop@^4.1.0, dot-prop@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" dependencies: @@ -3596,6 +4113,15 @@ duplexer@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.1.tgz#b1a7a29c4abfd639585efaecce80d666b1e34125" + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + eastasianwidth@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.1.1.tgz#44d656de9da415694467335365fb3147b8572b7c" @@ -3611,10 +4137,18 @@ ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" +ejs@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" + electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.47: version "1.3.55" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.55.tgz#f150e10b20b77d9d41afcca312efe0c3b1a7fdce" +electron-to-chromium@^1.3.100: + version "1.3.102" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.102.tgz#3ac43a037c8a63bca3dfa189eb3d90f097196787" + element-resize-detector@^1.1.12: version "1.1.14" resolved "https://registry.yarnpkg.com/element-resize-detector/-/element-resize-detector-1.1.14.tgz#af064a0a618a820ad570a95c5eec5b77be0128c1" @@ -3701,6 +4235,14 @@ engine.io@~3.2.0: engine.io-parser "~2.1.0" ws "~3.3.1" +enhanced-resolve@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + tapable "^1.0.0" + enhanced-resolve@~0.9.0: version "0.9.1" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" @@ -3782,7 +4324,7 @@ err-code@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" -errno@^0.1.3: +errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" dependencies: @@ -4163,6 +4705,18 @@ execa@^0.8.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + exenv@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" @@ -4269,6 +4823,41 @@ express@^4.13.3, express@^4.14.0: utils-merge "1.0.1" vary "~1.1.2" +express@^4.16.3: + version "4.16.4" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" + dependencies: + accepts "~1.3.5" + array-flatten "1.1.1" + body-parser "1.18.3" + content-disposition "0.5.2" + content-type "~1.0.4" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.1.1" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.2" + path-to-regexp "0.1.7" + proxy-addr "~2.0.4" + qs "6.5.2" + range-parser "~1.2.0" + safe-buffer "5.1.2" + send "0.16.2" + serve-static "1.13.2" + setprototypeof "1.1.0" + statuses "~1.4.0" + type-is "~1.6.16" + utils-merge "1.0.1" + vary "~1.1.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -4350,6 +4939,17 @@ fast-glob@^1.0.1: micromatch "^3.0.3" readdir-enhanced "^1.5.2" +fast-glob@^2.0.2: + version "2.2.6" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.6.tgz#a5d5b697ec8deda468d85a74035290a025a95295" + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -4398,6 +4998,10 @@ fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.9: setimmediate "^1.0.5" ua-parser-js "^0.7.18" +figgy-pudding@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" + figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -4417,29 +5021,17 @@ file-loader@^0.9.0: dependencies: loader-utils "~0.2.5" +file-loader@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-3.0.1.tgz#f8e0ba0b599918b51adfe45d66d1e771ad560faa" + dependencies: + loader-utils "^1.0.2" + schema-utils "^1.0.0" + filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" -filename-reserved-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz#e61cf805f0de1c984567d0386dc5df50ee5af7e4" - -filenamify-url@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/filenamify-url/-/filenamify-url-1.0.0.tgz#b32bd81319ef5863b73078bed50f46a4f7975f50" - dependencies: - filenamify "^1.0.0" - humanize-url "^1.0.0" - -filenamify@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-1.2.1.tgz#a9f2ffd11c503bed300015029272378f1f1365a5" - dependencies: - filename-reserved-regex "^1.0.0" - strip-outer "^1.0.0" - trim-repeated "^1.0.0" - fileset@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0" @@ -4451,6 +5043,10 @@ filesize@3.5.11: version "3.5.11" resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.5.11.tgz#1919326749433bb3cf77368bd158caabcc19e9ee" +filesize@^3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + fill-keys@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" @@ -4505,6 +5101,14 @@ find-cache-dir@^1.0.0: make-dir "^1.0.0" pkg-dir "^2.0.0" +find-cache-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.0.0.tgz#4c1faed59f45184530fb9d7fa123a4d04a98472d" + dependencies: + commondir "^1.0.1" + make-dir "^1.0.0" + pkg-dir "^3.0.0" + find-file-up@^0.1.2: version "0.1.3" resolved "https://registry.yarnpkg.com/find-file-up/-/find-file-up-0.1.3.tgz#cf68091bcf9f300a40da411b37da5cce5a2fbea0" @@ -4538,6 +5142,12 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + dependencies: + locate-path "^3.0.0" + findup-sync@0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.2.tgz#a8117d0f73124f5a4546839579fe52d7129fb5e5" @@ -4625,6 +5235,13 @@ flow-coverage-report@^0.5.0: terminal-table "0.0.12" yargs "8.0.1" +flush-write-stream@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.4" + fn-name@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-2.0.1.tgz#5214d7537a4d06a4a301c0cc262feb84188002e7" @@ -4704,6 +5321,13 @@ friendly-errors-webpack-plugin@^1.6.1: error-stack-parser "^2.0.0" string-width "^2.0.0" +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + front-matter@^2.1.0, front-matter@^2.1.2: version "2.3.0" resolved "https://registry.yarnpkg.com/front-matter/-/front-matter-2.3.0.tgz#7203af896ce357ee04e2aa45169ea91ed7f67504" @@ -4726,14 +5350,6 @@ fs-extra@^4.0.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b" @@ -4752,11 +5368,20 @@ fs-readdir-recursive@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" -fsevents@^1.0.0, fsevents@^1.2.3: +fsevents@^1.0.0, fsevents@^1.2.2, fsevents@^1.2.3: version "1.2.4" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" dependencies: @@ -5120,6 +5745,12 @@ get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + dependencies: + pump "^3.0.0" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -5130,18 +5761,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -gh-pages@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/gh-pages/-/gh-pages-1.2.0.tgz#1acb92801078f7c038a167f447221d1496ccfbee" - dependencies: - async "2.6.1" - commander "2.15.1" - filenamify-url "^1.0.0" - fs-extra "^5.0.0" - globby "^6.1.0" - graceful-fs "4.1.11" - rimraf "^2.6.2" - git-raw-commits@^1.3.0, git-raw-commits@^1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-1.3.6.tgz#27c35a32a67777c1ecd412a239a6c19d71b95aff" @@ -5256,6 +5875,17 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, gl once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-dirs@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" @@ -5332,6 +5962,18 @@ globby@^6.0.0, globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +globby@^8.0.1: + version "8.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" + dependencies: + array-union "^1.0.1" + dir-glob "2.0.0" + fast-glob "^2.0.2" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + globule@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d" @@ -5381,10 +6023,14 @@ got@^7.1.0: url-parse-lax "^1.0.0" url-to-options "^1.0.1" -graceful-fs@4.1.11, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.4, graceful-fs@^4.1.6: +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.4, graceful-fs@^4.1.6: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" +graceful-fs@^4.1.15: + version "4.1.15" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" + "graceful-readlink@>= 1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" @@ -5429,6 +6075,13 @@ gzip-size@3.0.0: dependencies: duplexer "^0.1.1" +gzip-size@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.0.0.tgz#a55ecd99222f4c48fd8c01c625ce3b349d0a0e80" + dependencies: + duplexer "^0.1.1" + pify "^3.0.0" + handlebars@^4.0.11, handlebars@^4.0.2, handlebars@^4.0.3: version "4.0.11" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" @@ -5542,7 +6195,7 @@ has-yarn@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-1.0.0.tgz#89e25db604b725c8f5976fff0addc921b828a5a7" -has@^1.0.1, has@^1.0.3: +has@^1.0.0, has@^1.0.1, has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" dependencies: @@ -5652,6 +6305,10 @@ hawk@~3.1.3: hoek "2.x.x" sntp "1.x.x" +hex-color-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + history@^4.6.2, history@^4.7.2: version "4.7.2" resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b" @@ -5695,10 +6352,22 @@ homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" +hoopy@^0.1.2: + version "0.1.4" + resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" + hosted-git-info@^2.1.4, hosted-git-info@^2.5.0: version "2.7.1" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" +hsl-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + +hsla-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + html-comment-regex@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" @@ -5746,7 +6415,7 @@ http-errors@1.6.2: setprototypeof "1.0.3" statuses ">= 1.3.1 < 2" -http-errors@1.6.3, http-errors@~1.6.2: +http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: version "1.6.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" dependencies: @@ -5829,13 +6498,6 @@ hullabaloo-config-manager@^1.1.0: resolve-from "^3.0.0" safe-buffer "^5.0.1" -humanize-url@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/humanize-url/-/humanize-url-1.0.1.tgz#f4ab99e0d288174ca4e1e50407c55fbae464efff" - dependencies: - normalize-url "^1.0.0" - strip-url-auth "^1.0.0" - iconv-lite@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" @@ -5850,10 +6512,20 @@ icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" +icss-utils@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.0.0.tgz#d52cf4bcdcfa1c45c2dbefb4ffdf6b00ef608098" + dependencies: + postcss "^7.0.5" + ieee754@^1.1.4: version "1.1.12" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + ignore-by-default@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" @@ -5864,7 +6536,7 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" -ignore@^3.3.3: +ignore@^3.3.3, ignore@^3.3.5: version "3.3.10" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" @@ -5880,6 +6552,13 @@ immutable@~3.7.6: version "3.7.6" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + import-lazy@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" @@ -5998,6 +6677,10 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + ipaddr.js@1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" @@ -6048,6 +6731,10 @@ is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" @@ -6078,6 +6765,17 @@ is-ci@^1.0.10, is-ci@^1.0.7: dependencies: ci-info "^1.0.0" +is-color-stop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" + dependencies: + css-color-names "^0.0.4" + hex-color-regex "^1.1.0" + hsl-regex "^1.0.0" + hsla-regex "^1.0.0" + rgb-regex "^1.0.1" + rgba-regex "^1.0.0" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -6114,6 +6812,10 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -6341,6 +7043,12 @@ is-svg@^2.0.0: dependencies: html-comment-regex "^1.1.0" +is-svg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" + dependencies: + html-comment-regex "^1.1.0" + is-symbol@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" @@ -6933,6 +7641,13 @@ js-yaml@^3.10.0, js-yaml@^3.11.0, js-yaml@^3.5.2, js-yaml@^3.7.0, js-yaml@^3.9.1 argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^3.12.0, js-yaml@^3.9.0: + version "3.12.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@~2.1.0: version "2.1.3" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-2.1.3.tgz#0ffb5617be55525878063d7a16aee7fdd282e84c" @@ -7002,7 +7717,7 @@ json-loader@^0.5.2: version "0.5.7" resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" -json-parse-better-errors@^1.0.1: +json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -7040,6 +7755,12 @@ json5@^0.5.0, json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + dependencies: + minimist "^1.2.0" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -7166,6 +7887,13 @@ kleur@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/kleur/-/kleur-2.0.1.tgz#7cc64b0d188d0dcbc98bdcdfdda2cc10619ddce8" +last-call-webpack-plugin@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555" + dependencies: + lodash "^4.17.5" + webpack-sources "^1.1.0" + last-line-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/last-line-stream/-/last-line-stream-1.0.0.tgz#d1b64d69f86ff24af2d04883a2ceee14520a5600" @@ -7188,6 +7916,12 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + dependencies: + invert-kv "^2.0.0" + left-pad@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" @@ -7304,6 +8038,10 @@ load-pkg@^3.0.1: dependencies: find-pkg "^0.1.0" +loader-runner@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.1.tgz#026f12fe7c3115992896ac02ba022ba92971b979" + loader-utils@^0.2.11, loader-utils@^0.2.15, loader-utils@^0.2.16, loader-utils@^0.2.3, loader-utils@~0.2.5: version "0.2.17" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" @@ -7321,6 +8059,14 @@ loader-utils@^1.0.2, loader-utils@^1.1.0: emojis-list "^2.0.0" json5 "^0.5.0" +loader-utils@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + dependencies: + big.js "^5.2.2" + emojis-list "^2.0.0" + json5 "^1.0.1" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -7328,6 +8074,13 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + lodash-es@^4.2.1: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.10.tgz#62cd7104cdf5dd87f235a837f0ede0e8e5117e05" @@ -7384,7 +8137,7 @@ lodash.clonedeepwith@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz#6ee30573a03a1a60d670a62ef33c10cf1afdbdd4" -lodash.debounce@^4.0.3: +lodash.debounce@^4.0.3, lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -7550,6 +8303,10 @@ lodash@4.11.1: version "4.11.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.11.1.tgz#a32106eb8e2ec8e82c241611414773c9df15f8bc" +lodash@^4.17.11: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + log-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" @@ -7601,6 +8358,12 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + dependencies: + yallist "^3.0.2" + ltcdr@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ltcdr/-/ltcdr-2.2.1.tgz#5ab87ad1d4c1dab8e8c08bbf037ee0c1902287cf" @@ -7623,6 +8386,12 @@ makeerror@1.0.x: dependencies: tmpl "1.0.x" +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + dependencies: + p-defer "^1.0.0" + map-cache@^0.2.0, map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -7766,6 +8535,10 @@ mdast-util-toc@^2.0.1: mdast-util-to-string "^1.0.2" unist-util-visit "^1.1.0" +mdn-data@~1.1.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-1.1.4.tgz#50b5d4ffc4575276573c4eedb8780812a8419f01" + mdurl@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" @@ -7799,20 +8572,28 @@ mem@^1.1.0: dependencies: mimic-fn "^1.0.0" +mem@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.0.0.tgz#6437690d9471678f6cc83659c00cbafcd6b0cdaf" + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^1.0.0" + p-is-promise "^1.1.0" + memory-fs@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290" -memory-fs@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.3.0.tgz#7bcc6b629e3a43e871d7e29aca6ae8a7f15cbb20" +memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" dependencies: errno "^0.1.3" readable-stream "^2.0.1" -memory-fs@~0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" +memory-fs@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.3.0.tgz#7bcc6b629e3a43e871d7e29aca6ae8a7f15cbb20" dependencies: errno "^0.1.3" readable-stream "^2.0.1" @@ -7862,6 +8643,10 @@ merge-stream@^1.0.1: dependencies: readable-stream "^2.0.1" +merge2@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.3.tgz#7ee99dbd69bb6481689253f018488a1b902b0ed5" + merge@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" @@ -8011,6 +8796,21 @@ minizlib@^1.1.0: dependencies: minipass "^2.2.1" +mississippi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^3.0.0" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + mitt@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/mitt/-/mitt-1.1.3.tgz#528c506238a05dce11cd914a741ea2cc332da9b8" @@ -8044,6 +8844,17 @@ moo@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/moo/-/moo-0.4.3.tgz#3f847a26f31cf625a956a87f2b10fbc013bfd10e" +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -8119,6 +8930,10 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +neo-async@^2.5.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835" + netrc@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/netrc/-/netrc-0.1.4.tgz#6be94fcaca8d77ade0a9670dc460914c94472444" @@ -8263,6 +9078,12 @@ node-pre-gyp@^0.10.0: semver "^5.3.0" tar "^4" +node-releases@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.3.tgz#aad9ce0dcb98129c753f772c0aa01360fb90fbd2" + dependencies: + semver "^5.3.0" + node-sass@^4.5.2: version "4.9.2" resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.2.tgz#5e63fe6bd0f2ae3ac9d6c14ede8620e2b8bdb437" @@ -8333,7 +9154,7 @@ normalize-range@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" -normalize-url@^1.0.0, normalize-url@^1.4.0: +normalize-url@^1.4.0: version "1.9.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" dependencies: @@ -8342,6 +9163,10 @@ normalize-url@^1.0.0, normalize-url@^1.4.0: query-string "^4.1.0" sort-keys "^1.0.0" +normalize-url@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" + npm-bundled@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308" @@ -8382,6 +9207,12 @@ nth-check@^1.0.1, nth-check@~1.0.1: dependencies: boolbase "~1.0.0" +nth-check@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + dependencies: + boolbase "~1.0.0" + null-loader@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/null-loader/-/null-loader-0.1.1.tgz#17be9abfcd3ff0e1512f6fc4afcb1f5039378fae" @@ -8572,6 +9403,10 @@ open@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/open/-/open-0.0.5.tgz#42c3e18ec95466b6bf0dc42f3a2945c3f0cad8fc" +opener@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" + opn@5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.1.0.tgz#72ce2306a17dbea58ff1041853352b4a8fc77519" @@ -8591,6 +9426,13 @@ optimist@^0.6.1, optimist@~0.6.0, optimist@~0.6.1: minimist "~0.0.1" wordwrap "~0.0.2" +optimize-css-assets-webpack-plugin@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.1.tgz#9eb500711d35165b45e7fd60ba2df40cb3eb9159" + dependencies: + cssnano "^4.1.0" + last-call-webpack-plugin "^3.0.0" + option-chain@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/option-chain/-/option-chain-1.0.0.tgz#938d73bd4e1783f948d34023644ada23669e30f2" @@ -8638,6 +9480,14 @@ os-locale@^2.0.0: lcid "^1.0.0" mem "^1.1.0" +os-locale@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -8661,22 +9511,42 @@ p-cancelable@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" +p-is-promise@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" + p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" dependencies: p-try "^1.0.0" +p-limit@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.1.0.tgz#1d5a0d20fb12707c758a655f6bbc4386b5930d68" + dependencies: + p-try "^2.0.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" dependencies: p-limit "^1.1.0" +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + dependencies: + p-limit "^2.0.0" + p-map@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" @@ -8691,6 +9561,10 @@ p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" +p-try@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" + package-hash@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-1.2.0.tgz#003e56cd57b736a6ed6114cc2b81542672770e44" @@ -8723,6 +9597,14 @@ pako@~1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" +parallel-transform@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" + dependencies: + cyclist "~0.2.2" + inherits "^2.0.3" + readable-stream "^2.1.5" + parse-asn1@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" @@ -9009,6 +9891,12 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + dependencies: + find-up "^3.0.0" + pkg-resolve@^0.1.7: version "0.1.14" resolved "https://registry.yarnpkg.com/pkg-resolve/-/pkg-resolve-0.1.14.tgz#329b2e76ccbb372e22e6a3a41cb30ab0457836ba" @@ -9069,6 +9957,15 @@ postcss-calc@^5.0.0, postcss-calc@^5.2.0: postcss-message-helpers "^2.0.0" reduce-css-calc "^1.2.6" +postcss-calc@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.1.tgz#36d77bab023b0ecbb9789d84dcb23c4941145436" + dependencies: + css-unit-converter "^1.1.1" + postcss "^7.0.5" + postcss-selector-parser "^5.0.0-rc.4" + postcss-value-parser "^3.3.1" + postcss-color-function@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/postcss-color-function/-/postcss-color-function-2.0.1.tgz#9ad226f550e8a7c7f8b8a77860545b6dd7f55241" @@ -9142,6 +10039,16 @@ postcss-colormin@^2.1.8: postcss "^5.0.13" postcss-value-parser "^3.2.3" +postcss-colormin@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.2.tgz#93cd1fa11280008696887db1a528048b18e7ed99" + dependencies: + browserslist "^4.0.0" + color "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + postcss-convert-values@^2.3.4: version "2.6.1" resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d" @@ -9149,6 +10056,13 @@ postcss-convert-values@^2.3.4: postcss "^5.0.11" postcss-value-parser "^3.1.2" +postcss-convert-values@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + postcss-cssnext@^2.8.0: version "2.11.0" resolved "https://registry.yarnpkg.com/postcss-cssnext/-/postcss-cssnext-2.11.0.tgz#31e68f001e409604da703b66de14b8b8c8c9f2b1" @@ -9212,24 +10126,48 @@ postcss-discard-comments@^2.0.4: dependencies: postcss "^5.0.14" +postcss-discard-comments@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.1.tgz#30697735b0c476852a7a11050eb84387a67ef55d" + dependencies: + postcss "^7.0.0" + postcss-discard-duplicates@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932" dependencies: postcss "^5.0.4" +postcss-discard-duplicates@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" + dependencies: + postcss "^7.0.0" + postcss-discard-empty@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5" dependencies: postcss "^5.0.14" +postcss-discard-empty@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" + dependencies: + postcss "^7.0.0" + postcss-discard-overridden@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58" dependencies: postcss "^5.0.16" +postcss-discard-overridden@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" + dependencies: + postcss "^7.0.0" + postcss-discard-unused@^2.2.1: version "2.2.3" resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433" @@ -9315,6 +10253,15 @@ postcss-merge-longhand@^2.0.1: dependencies: postcss "^5.0.4" +postcss-merge-longhand@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.10.tgz#c4d63ab57bdc054ab4067ab075d488c8c2978380" + dependencies: + css-color-names "0.0.4" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + stylehacks "^4.0.0" + postcss-merge-rules@^2.0.3: version "2.1.2" resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721" @@ -9325,6 +10272,17 @@ postcss-merge-rules@^2.0.3: postcss-selector-parser "^2.2.2" vendors "^1.0.0" +postcss-merge-rules@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.2.tgz#2be44401bf19856f27f32b8b12c0df5af1b88e74" + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + cssnano-util-same-parent "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + vendors "^1.0.0" + postcss-message-helpers@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e" @@ -9337,6 +10295,13 @@ postcss-minify-font-values@^1.0.2: postcss "^5.0.4" postcss-value-parser "^3.0.2" +postcss-minify-font-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + postcss-minify-gradients@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1" @@ -9344,6 +10309,15 @@ postcss-minify-gradients@^1.0.1: postcss "^5.0.12" postcss-value-parser "^3.3.0" +postcss-minify-gradients@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.1.tgz#6da95c6e92a809f956bb76bf0c04494953e1a7dd" + dependencies: + cssnano-util-get-arguments "^4.0.0" + is-color-stop "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + postcss-minify-params@^1.0.4: version "1.2.2" resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3" @@ -9353,6 +10327,17 @@ postcss-minify-params@^1.0.4: postcss-value-parser "^3.0.2" uniqs "^2.0.0" +postcss-minify-params@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.1.tgz#5b2e2d0264dd645ef5d68f8fec0d4c38c1cf93d2" + dependencies: + alphanum-sort "^1.0.0" + browserslist "^4.0.0" + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + uniqs "^2.0.0" + postcss-minify-selectors@^2.0.4: version "2.1.1" resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf" @@ -9362,12 +10347,27 @@ postcss-minify-selectors@^2.0.4: postcss "^5.0.14" postcss-selector-parser "^2.0.0" +postcss-minify-selectors@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.1.tgz#a891c197977cc37abf60b3ea06b84248b1c1e9cd" + dependencies: + alphanum-sort "^1.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + postcss-modules-extract-imports@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz#b614c9720be6816eaee35fb3a5faa1dba6a05ddb" dependencies: postcss "^6.0.1" +postcss-modules-extract-imports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" + dependencies: + postcss "^7.0.5" + postcss-modules-local-by-default@^1.0.1: version "1.2.0" resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" @@ -9375,6 +10375,14 @@ postcss-modules-local-by-default@^1.0.1: css-selector-tokenizer "^0.7.0" postcss "^6.0.1" +postcss-modules-local-by-default@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.4.tgz#a000bb07e4f57f412ba35c904d035cfd4a7b9446" + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^7.0.6" + postcss-value-parser "^3.3.1" + postcss-modules-scope@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" @@ -9382,6 +10390,13 @@ postcss-modules-scope@^1.0.0: css-selector-tokenizer "^0.7.0" postcss "^6.0.1" +postcss-modules-scope@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.0.1.tgz#2c0f2394cde4cd09147db054c68917e38f6d43a4" + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^7.0.6" + postcss-modules-values@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" @@ -9389,6 +10404,13 @@ postcss-modules-values@^1.1.0: icss-replace-symbols "^1.1.0" postcss "^6.0.1" +postcss-modules-values@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz#479b46dc0c5ca3dc7fa5270851836b9ec7152f64" + dependencies: + icss-replace-symbols "^1.1.0" + postcss "^7.0.6" + postcss-nesting@^2.0.5: version "2.3.1" resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-2.3.1.tgz#94a6b6a4ef707fbec20a87fee5c957759b4e01cf" @@ -9401,6 +10423,62 @@ postcss-normalize-charset@^1.1.0: dependencies: postcss "^5.0.5" +postcss-normalize-charset@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" + dependencies: + postcss "^7.0.0" + +postcss-normalize-display-values@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.1.tgz#d9a83d47c716e8a980f22f632c8b0458cfb48a4c" + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-positions@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.1.tgz#ee2d4b67818c961964c6be09d179894b94fd6ba1" + dependencies: + cssnano-util-get-arguments "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-repeat-style@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.1.tgz#5293f234b94d7669a9f805495d35b82a581c50e5" + dependencies: + cssnano-util-get-arguments "^4.0.0" + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-string@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.1.tgz#23c5030c2cc24175f66c914fa5199e2e3c10fef3" + dependencies: + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-timing-functions@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.1.tgz#8be83e0b9cb3ff2d1abddee032a49108f05f95d7" + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-unicode@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + postcss-normalize-url@^3.0.7: version "3.0.8" resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222" @@ -9410,6 +10488,22 @@ postcss-normalize-url@^3.0.7: postcss "^5.0.14" postcss-value-parser "^3.2.3" +postcss-normalize-url@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-whitespace@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.1.tgz#d14cb639b61238418ac8bc8d3b7bdd65fc86575e" + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + postcss-ordered-values@^2.1.0: version "2.2.3" resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d" @@ -9417,6 +10511,14 @@ postcss-ordered-values@^2.1.0: postcss "^5.0.4" postcss-value-parser "^3.0.1" +postcss-ordered-values@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.1.tgz#2e3b432ef3e489b18333aeca1f1295eb89be9fc2" + dependencies: + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + postcss-pseudo-class-any-link@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-1.0.0.tgz#903239196401d335fe73ac756186fa62e693af26" @@ -9443,6 +10545,15 @@ postcss-reduce-initial@^1.0.0: dependencies: postcss "^5.0.4" +postcss-reduce-initial@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.2.tgz#bac8e325d67510ee01fa460676dc8ea9e3b40f15" + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-reduce-transforms@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1" @@ -9451,6 +10562,15 @@ postcss-reduce-transforms@^1.0.3: postcss "^5.0.8" postcss-value-parser "^3.0.1" +postcss-reduce-transforms@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.1.tgz#8600d5553bdd3ad640f43bff81eb52f8760d4561" + dependencies: + cssnano-util-get-match "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + postcss-replace-overflow-wrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-1.0.0.tgz#f0a03b31eab9636a6936bfd210e2aef1b434a643" @@ -9496,6 +10616,22 @@ postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.0, postcss-selector indexes-of "^1.0.1" uniq "^1.0.1" +postcss-selector-parser@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865" + dependencies: + dot-prop "^4.1.1" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^5.0.0-rc.4: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" + dependencies: + cssesc "^2.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + postcss-svgo@^2.1.1: version "2.1.6" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d" @@ -9505,6 +10641,15 @@ postcss-svgo@^2.1.1: postcss-value-parser "^3.2.3" svgo "^0.7.0" +postcss-svgo@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.1.tgz#5628cdb38f015de6b588ce6d0bf0724b492b581d" + dependencies: + is-svg "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + svgo "^1.0.0" + postcss-unique-selectors@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d" @@ -9513,6 +10658,18 @@ postcss-unique-selectors@^2.0.2: postcss "^5.0.4" uniqs "^2.0.0" +postcss-unique-selectors@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" + dependencies: + alphanum-sort "^1.0.0" + postcss "^7.0.0" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" @@ -9542,6 +10699,14 @@ postcss@^6.0.1, postcss@^6.0.14: source-map "^0.6.1" supports-color "^5.4.0" +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.5, postcss@^7.0.6: + version "7.0.11" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.11.tgz#f63c513b78026d66263bb2ca995bf02e3d1a697d" + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -9616,6 +10781,10 @@ promise-each@^2.2.0: dependencies: any-promise "^0.1.0" +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + promise@^7.1.1: version "7.3.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" @@ -9656,7 +10825,7 @@ property-information@^3.0.0, property-information@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-3.2.0.tgz#fd1483c8fbac61808f5fe359e7693a1f48a58331" -proxy-addr@~2.0.3: +proxy-addr@~2.0.3, proxy-addr@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" dependencies: @@ -9700,6 +10869,28 @@ pump@^1.0.0: end-of-stream "^1.1.0" once "^1.3.1" +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -9724,14 +10915,14 @@ qs@6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" +qs@6.5.2, qs@~6.5.1: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" -qs@~6.5.1: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - query-string@^4.1.0: version "4.3.4" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" @@ -9806,7 +10997,7 @@ raw-body@2.3.2: iconv-lite "0.4.19" unpipe "1.0.0" -raw-body@^2.3.2: +raw-body@2.3.3, raw-body@^2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" dependencies: @@ -9999,6 +11190,14 @@ react-side-effect@^1.1.0: exenv "^1.2.1" shallowequal "^1.0.1" +react-sortable-hoc@1.4.0, react-sortable-hoc@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-1.4.0.tgz#b477ce700ba755754200a1dabd36e588e2f5608d" + dependencies: + "@babel/runtime" "^7.2.0" + invariant "^2.2.4" + prop-types "^15.5.7" + react-spruce@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/react-spruce/-/react-spruce-0.1.0.tgz#e1b048e5e5c445b867215be2c48fff034a38f667" @@ -10084,6 +11283,13 @@ read-pkg-up@^3.0.0: find-up "^2.0.0" read-pkg "^3.0.0" +read-pkg-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" + dependencies: + find-up "^3.0.0" + read-pkg "^3.0.0" + read-pkg@^1.0.0, read-pkg@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" @@ -10114,16 +11320,7 @@ read@^1.0.7: dependencies: mute-stream "~0.0.4" -readable-stream@1.0, readable-stream@~1.0.31: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" dependencies: @@ -10135,6 +11332,15 @@ readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@1.0, readable-stream@~1.0.31: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + readdir-enhanced@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/readdir-enhanced/-/readdir-enhanced-1.5.2.tgz#61463048690ac6a455b75b62fa78a88f8dc85e53" @@ -10235,6 +11441,10 @@ regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" +regenerator-runtime@^0.12.0: + version "0.12.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" + regenerator-transform@^0.10.0: version "0.10.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" @@ -10665,10 +11875,18 @@ rgb-hex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/rgb-hex/-/rgb-hex-1.0.0.tgz#bfaf8cd9cd9164b5a26d71eb4f15a0965324b3c1" +rgb-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + rgb@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/rgb/-/rgb-0.1.0.tgz#be27b291e8feffeac1bd99729721bfa40fc037b5" +rgba-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + ric@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/ric/-/ric-1.3.0.tgz#8e95042609ce8213548a83164d08e94fae94909f" @@ -10728,6 +11946,12 @@ run-async@^2.2.0: dependencies: is-promise "^2.1.0" +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + dependencies: + aproba "^1.1.1" + rx-lite-aggregates@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" @@ -10829,7 +12053,7 @@ sass-loader@^4.1.1: loader-utils "^0.2.15" object-assign "^4.1.0" -sax@^1.2.4, sax@~1.2.1: +sax@^1.2.4, sax@~1.2.1, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -10869,6 +12093,21 @@ schema-utils@^0.3.0: dependencies: ajv "^5.0.0" +schema-utils@^0.4.4: + version "0.4.7" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + dependencies: + ajv "^6.1.0" + ajv-errors "^1.0.0" + ajv-keywords "^3.1.0" + scroll-behavior@^0.9.9: version "0.9.9" resolved "https://registry.yarnpkg.com/scroll-behavior/-/scroll-behavior-0.9.9.tgz#ebfe0658455b82ad885b66195215416674dacce2" @@ -10927,6 +12166,10 @@ serialize-error@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" +serialize-javascript@^1.4.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.6.1.tgz#4d1f697ec49429a847ca6f442a2a755126c4d879" + serve-handler@3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-3.6.0.tgz#f4167c06d0a26c38ed753418230ed43a59adf9cb" @@ -11084,6 +12327,12 @@ signedsource@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a" +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + dependencies: + is-arrayish "^0.3.1" + sinon@^5.0.7: version "5.1.1" resolved "https://registry.yarnpkg.com/sinon/-/sinon-5.1.1.tgz#19c59810ffb733ea6e76a28b94a71fc4c2f523b8" @@ -11100,6 +12349,28 @@ sisteransi@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-0.1.1.tgz#5431447d5f7d1675aac667ccd0b865a4994cb3ce" +size-limit@^0.21.1: + version "0.21.1" + resolved "https://registry.yarnpkg.com/size-limit/-/size-limit-0.21.1.tgz#755ecd318a02570574ff7a815b88c6801f9a9995" + dependencies: + bytes "^3.0.0" + chalk "^2.4.1" + ci-job-number "^0.3.0" + compression-webpack-plugin "^2.0.0" + cosmiconfig "^5.0.7" + css-loader "^2.1.0" + escape-string-regexp "^1.0.5" + file-loader "^3.0.1" + globby "^8.0.1" + gzip-size "^5.0.0" + memory-fs "^0.4.1" + optimize-css-assets-webpack-plugin "^5.0.1" + read-pkg-up "^4.0.0" + style-loader "^0.23.1" + webpack "^4.28.2" + webpack-bundle-analyzer "^3.0.3" + yargs "^12.0.5" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -11256,6 +12527,10 @@ source-list-map@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc069498337ef6186a11a1" +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + source-map-resolve@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" @@ -11285,6 +12560,13 @@ source-map-support@~0.2.8: dependencies: source-map "0.1.32" +source-map-support@~0.5.6: + version "0.5.10" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.10.tgz#2214080bc9d51832511ee2bab96e3c2f9353120c" + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" @@ -11412,6 +12694,16 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" +ssri@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" + dependencies: + figgy-pudding "^3.5.1" + +stable@~0.1.6: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + stack-trace@^0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" @@ -11520,6 +12812,13 @@ stream-cache@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/stream-cache/-/stream-cache-0.0.2.tgz#1ac5ad6832428ca55667dbdee395dad4e6db118f" +stream-each@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + stream-http@^2.3.1, stream-http@^2.7.2: version "2.8.3" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" @@ -11530,6 +12829,10 @@ stream-http@^2.3.1, stream-http@^2.7.2: to-arraybuffer "^1.0.0" xtend "^4.0.0" +stream-shift@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -11649,16 +12952,6 @@ strip-json-comments@2.0.1, strip-json-comments@^2.0.1, strip-json-comments@~2.0. version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" -strip-outer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" - dependencies: - escape-string-regexp "^1.0.2" - -strip-url-auth@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-url-auth/-/strip-url-auth-1.0.1.tgz#22b0fa3a41385b33be3f331551bbb837fa0cd7ae" - strong-log-transformer@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-1.0.6.tgz#f7fb93758a69a571140181277eea0c2eb1301fa3" @@ -11675,6 +12968,21 @@ style-loader@^0.13.0: dependencies: loader-utils "^1.0.2" +style-loader@^0.23.1: + version "0.23.1" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.23.1.tgz#cb9154606f3e771ab6c4ab637026a1049174d925" + dependencies: + loader-utils "^1.1.0" + schema-utils "^1.0.0" + +stylehacks@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.1.tgz#3186595d047ab0df813d213e51c8b94e0b9010f2" + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + supertap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supertap/-/supertap-1.0.0.tgz#bd9751c7fafd68c68cf8222a29892206a119fa9e" @@ -11701,6 +13009,12 @@ supports-color@^5.0.0, supports-color@^5.3.0, supports-color@^5.4.0: dependencies: has-flag "^3.0.0" +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + dependencies: + has-flag "^3.0.0" + svgo@^0.7.0: version "0.7.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" @@ -11713,6 +13027,25 @@ svgo@^0.7.0: sax "~1.2.1" whet.extend "~0.9.9" +svgo@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.1.1.tgz#12384b03335bcecd85cfa5f4e3375fed671cb985" + dependencies: + coa "~2.0.1" + colors "~1.1.2" + css-select "^2.0.0" + css-select-base-adapter "~0.1.0" + css-tree "1.0.0-alpha.28" + css-url-regex "^1.1.0" + csso "^3.5.0" + js-yaml "^3.12.0" + mkdirp "~0.5.1" + object.values "^1.0.4" + sax "~1.2.4" + stable "~0.1.6" + unquote "~1.1.1" + util.promisify "~1.0.0" + svgo@~0.4.5: version "0.4.5" resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.4.5.tgz#ba56155fb1733728956c01b405221ee7e789a2a4" @@ -11801,6 +13134,10 @@ tapable@^0.1.8, tapable@~0.1.8: version "0.1.10" resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" +tapable@^1.0.0, tapable@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.1.tgz#4d297923c5a72a42360de2ab52dadfaaec00018e" + tar-fs@^1.13.0: version "1.16.3" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" @@ -11884,6 +13221,27 @@ terminal-table@0.0.12: colors "^1.0.3" eastasianwidth "^0.1.0" +terser-webpack-plugin@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.1.tgz#7545da9ae5f4f9ae6a0ac961eb46f5e7c845cc26" + dependencies: + cacache "^11.0.2" + find-cache-dir "^2.0.0" + schema-utils "^1.0.0" + serialize-javascript "^1.4.0" + source-map "^0.6.1" + terser "^3.8.1" + webpack-sources "^1.1.0" + worker-farm "^1.5.2" + +terser@^3.8.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-3.14.1.tgz#cc4764014af570bc79c79742358bd46926018a32" + dependencies: + commander "~2.17.1" + source-map "~0.6.1" + source-map-support "~0.5.6" + test-exclude@^4.2.0, test-exclude@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.1.tgz#dfa222f03480bca69207ca728b37d74b45f724fa" @@ -11939,6 +13297,10 @@ timers-browserify@^2.0.2, timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + tiny-emitter@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c" @@ -12062,12 +13424,6 @@ trim-off-newlines@^1.0.0, trim-off-newlines@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" -trim-repeated@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" - dependencies: - escape-string-regexp "^1.0.2" - trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" @@ -12090,7 +13446,11 @@ trough@^1.0.0: dependencies: glob "^6.0.4" -tslib@^1.6.0: +tryer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" + +tslib@^1.6.0, tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" @@ -12229,6 +13589,18 @@ uniqs@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.1.tgz#5e9edc6d1ce8fb264db18a507ef9bd8544451ca6" + dependencies: + imurmurhash "^0.1.4" + unique-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" @@ -12335,10 +13707,23 @@ unmutable@^0.39.0: is-plain-object "^2.0.4" lodash.range "^3.2.0" +unmutable@^0.41.1: + version "0.41.1" + resolved "https://registry.yarnpkg.com/unmutable/-/unmutable-0.41.1.tgz#5d67409c6001108ab7deba236004e40d515dd2c7" + dependencies: + "@babel/runtime" "^7.1.5" + fast-deep-equal "^1.0.0" + is-plain-object "^2.0.4" + lodash.range "^3.2.0" + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -12350,6 +13735,10 @@ unzip-response@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" +upath@^1.0.5: + version "1.1.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" + update-check@1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.2.tgz#2fe09f725c543440b3d7dabe8971f2d5caaedc28" @@ -12372,7 +13761,7 @@ update-notifier@^2.3.0: semver-diff "^2.0.0" xdg-basedir "^3.0.0" -uri-js@^4.2.1: +uri-js@^4.2.1, uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" dependencies: @@ -12430,7 +13819,7 @@ util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -util.promisify@^1.0.0: +util.promisify@^1.0.0, util.promisify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" dependencies: @@ -12582,6 +13971,14 @@ watchpack@^0.2.1: chokidar "^1.0.0" graceful-fs "^4.1.2" +watchpack@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" + dependencies: + chokidar "^2.0.2" + graceful-fs "^4.1.2" + neo-async "^2.5.0" + wcwidth@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -12596,6 +13993,23 @@ webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" +webpack-bundle-analyzer@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.0.3.tgz#dbc7fff8f52058b6714a20fddf309d0790e3e0a0" + dependencies: + acorn "^5.7.3" + bfj "^6.1.1" + chalk "^2.4.1" + commander "^2.18.0" + ejs "^2.6.1" + express "^4.16.3" + filesize "^3.6.1" + gzip-size "^5.0.0" + lodash "^4.17.10" + mkdirp "^0.5.1" + opener "^1.5.1" + ws "^6.0.0" + webpack-configurator@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/webpack-configurator/-/webpack-configurator-0.3.1.tgz#d16802afa674101a0cbfa6fc344d415c9649540b" @@ -12672,6 +14086,13 @@ webpack-sources@^0.2.0: source-list-map "^1.1.1" source-map "~0.5.3" +webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-sources@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + webpack-stats-plugin@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-0.1.5.tgz#29e5f12ebfd53158d31d656a113ac1f7b86179d9" @@ -12712,6 +14133,35 @@ webpack@^1.13.3: watchpack "^0.2.1" webpack-core "~0.6.9" +webpack@^4.28.2: + version "4.28.4" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.28.4.tgz#1ddae6c89887d7efb752adf0c3cd32b9b07eacd0" + dependencies: + "@webassemblyjs/ast" "1.7.11" + "@webassemblyjs/helper-module-context" "1.7.11" + "@webassemblyjs/wasm-edit" "1.7.11" + "@webassemblyjs/wasm-parser" "1.7.11" + acorn "^5.6.2" + acorn-dynamic-import "^3.0.0" + ajv "^6.1.0" + ajv-keywords "^3.1.0" + chrome-trace-event "^1.0.0" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.0" + json-parse-better-errors "^1.0.2" + loader-runner "^2.3.0" + loader-utils "^1.1.0" + memory-fs "~0.4.1" + micromatch "^3.1.8" + mkdirp "~0.5.0" + neo-async "^2.5.0" + node-libs-browser "^2.0.0" + schema-utils "^0.4.4" + tapable "^1.1.0" + terser-webpack-plugin "^1.1.0" + watchpack "^1.5.0" + webpack-sources "^1.3.0" + websocket-driver@>=0.5.1: version "0.7.0" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb" @@ -12803,6 +14253,12 @@ wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" +worker-farm@^1.5.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0" + dependencies: + errno "~0.1.7" + wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -12867,6 +14323,12 @@ ws@^5.2.0: dependencies: async-limiter "~1.0.0" +ws@^6.0.0: + version "6.1.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.2.tgz#3cc7462e98792f0ac679424148903ded3b9c3ad8" + dependencies: + async-limiter "~1.0.0" + ws@~3.3.1: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" @@ -12903,6 +14365,10 @@ y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" +"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" @@ -12917,6 +14383,13 @@ yaml-loader@^0.4.0: dependencies: js-yaml "^3.5.2" +yargs-parser@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" @@ -13001,6 +14474,23 @@ yargs@8.0.1: y18n "^3.2.1" yargs-parser "^7.0.0" +yargs@^12.0.5: + version "12.0.5" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" + dependencies: + cliui "^4.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^1.0.1" + os-locale "^3.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1 || ^4.0.0" + yargs-parser "^11.1.1" + yargs@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"