diff --git a/.circleci/config.yml b/.circleci/config.yml index 014f5d4..2b7d611 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -39,7 +39,7 @@ jobs: - run: name: Run Type checking - command: npm run flow -s + command: npm run types -s - run: name: Run Unit Tests diff --git a/.eslintrc.js b/.eslintrc.js index 805c84e..dcc150d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,7 +7,8 @@ module.exports = { extends: [ 'eslint:recommended', 'plugin:react/recommended', - 'plugin:flowtype/recommended', + 'plugin:prettier/recommended', + 'prettier/react', ], parserOptions: { ecmaFeatures: { @@ -18,20 +19,59 @@ module.exports = { }, settings: { react: { - version: 'latest', + version: 'detect', }, }, - plugins: ['react', 'react-hooks', 'flowtype', 'import'], + globals: { + // fix for eslint-plugin-flowtype/384 not supporting wildcard + _: 'readonly' + }, + plugins: ['react', 'react-hooks', 'import'], rules: { 'no-shadow': ['error'], indent: ['off'], 'linebreak-style': ['off'], quotes: ['off'], semi: ['off'], + 'prettier/prettier': ['warn'], 'react/no-direct-mutation-state': ['off'], 'react/display-name': ['off'], 'react-hooks/rules-of-hooks': ['error'], 'react-hooks/exhaustive-deps': ['warn'], - 'flowtype/generic-spacing': ['off'], }, + + overrides: [ + { + // Flow specific rules + files: ['src/index.js.flow', '*/*flow.js', 'examples/*-flow/*/*.js'], + extends: ['plugin:flowtype/recommended'], + plugins: ['flowtype'], + rules: { + 'flowtype/generic-spacing': ['off'], + }, + }, + { + // TypeScript specific rules + files: ['*.{ts,tsx}'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'prettier/@typescript-eslint', + ], + rules: { + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-use-before-define': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/explicit-member-accessibility': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-empty-interface': 'off', + }, + }, + { + // Jest env + files: ['*.test.{js,ts,tsx}'], + env: { + jest: true, + }, + }, + ], }; diff --git a/.gitignore b/.gitignore index 0519409..ac5f476 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ node_modules lib coverage -yarn.lock \ No newline at end of file +yarn.lock + +# misc +.vscode \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..e8bfc66 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,7 @@ +module.exports = { + printWidth: 80, + tabWidth: 2, + semi: true, + singleQuote: true, + trailingComma: 'es5', +} diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 599477b..9bf0fe6 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -33,4 +33,5 @@ * **Recipes** - [Flow types](/recipes/flow.md) + - [Typescript types](/recipes/typescript.md) - [Composition](/recipes/composition.md) diff --git a/docs/recipes/README.md b/docs/recipes/README.md index 9497e05..d401c94 100644 --- a/docs/recipes/README.md +++ b/docs/recipes/README.md @@ -1,4 +1,5 @@ ## Recipes - [Flow types](./flow.md) +- [Typescript types](./typescript.md) - [Composition](./composition.md) diff --git a/docs/recipes/typescript.md b/docs/recipes/typescript.md new file mode 100644 index 0000000..3df8b2c --- /dev/null +++ b/docs/recipes/typescript.md @@ -0,0 +1,104 @@ +## Typing sweet-state with Typescript + +This is a basic example: + +```ts +import { + createStore, + createSubscriber, + createHook, + createContainer, + ActionApi, +} from 'react-sweet-state'; + +type State = { count: number }; +type Actions = typeof actions; + +const initialState: State = { + count: 0, +}; + +const actions = { + increment: (by = 1) => ({ setState, getState }: ActionApi) => { + setState({ + count: getState().count + by, + }); + }, +}; + +const Store = createStore({ + initialState, + actions, +}); + +const CounterSubscriber = createSubscriber(Store); +const useCounter = createHook(Store); +const CounterContainer = createContainer(Store); +``` + +You don't have to manually type all the `create*` methods, as they can be inferred for the simpler use cases. + +#### Actions patterns + +If your actions require `Container` props: + +```ts +type ContainerProps = { multiplier: number }; + +const actions = { + increment: (by = 1) => ( + { setState, getState }: ActionApi, + { multiplier }: ContainerProps + ) => { + setState({ count: getState().count + by * multiplier }); + }, +}; +``` + +#### createSubscriber / createHook patterns + +If you provide a selector to your components, you need to defined two additional flow arguments to `createSubscriber`/`createHook`: the selector output and the selector props. + +```js +type SelectorState = boolean; +const selector = (state: State): SelectorState => state.count > 0; + +// this component does not accept props +const CounterSubscriber = createSubscriber(Store, { + selector +}); + +// this hook does not accept arguments +const useCounter = createHook(Store, { + selector +}); +``` + +In case your component/hook needs also some props, you can define them as fourth argument: + +```js +type SelectorProps = { min: number }; +type SelectorState = boolean; +const selector = (state: State, props: SelectorProps): SelectorState => state.count > props.min; + +// this component requires props +const CounterSubscriber = createSubscriber(Store, { + selector +}); + +// this hook requires an argument +const useCounter = createHook(Store, { + selector +}); +``` + +#### createContainer patterns + +If your container requires additional props: + +```js +type ContainerProps = { multiplier: number }; + +// this component requires props +const CounterContainer = createContainer(Store); +``` diff --git a/examples/basic-flow/components.js b/examples/basic-flow/components.js index 6d03b22..6fa1088 100644 --- a/examples/basic-flow/components.js +++ b/examples/basic-flow/components.js @@ -7,9 +7,9 @@ import { type Action, } from 'react-sweet-state'; -type State = { +type State = {| count: number, -}; +|}; type Actions = typeof actions; diff --git a/examples/basic-ts/components.tsx b/examples/basic-ts/components.tsx new file mode 100644 index 0000000..9a9025f --- /dev/null +++ b/examples/basic-ts/components.tsx @@ -0,0 +1,33 @@ +import { + createStore, + createSubscriber, + createHook, + ActionApi, +} from 'react-sweet-state'; + +type State = { + count: number; +}; + +type Actions = typeof actions; + +const initialState: State = { + count: 0, +}; + +const actions = { + increment: () => ({ setState, getState }: ActionApi) => { + setState({ + count: getState().count + 1, + }); + }, +}; + +const Store = createStore({ + initialState, + actions, +}); + +export const CounterSubscriber = createSubscriber(Store); + +export const useCounter = createHook(Store); diff --git a/examples/basic-ts/index.html b/examples/basic-ts/index.html new file mode 100644 index 0000000..c3cf364 --- /dev/null +++ b/examples/basic-ts/index.html @@ -0,0 +1,23 @@ + + + + Advanced example with TypeScript + + + + +
+ + + diff --git a/examples/basic-ts/index.tsx b/examples/basic-ts/index.tsx new file mode 100644 index 0000000..483d6c3 --- /dev/null +++ b/examples/basic-ts/index.tsx @@ -0,0 +1,46 @@ +// @flow +import React from 'react'; +import ReactDOM from 'react-dom'; + +import '@babel/polyfill'; + +import { CounterSubscriber, useCounter } from './components'; + +const CounterHook = () => { + const [{ count }, { increment }] = useCounter(); + return ( +
+

With Hooks

+

{count}

+ +
+ ); +}; + +const CounterRpc = () => ( + + {({ count }, { increment }) => ( +
+

With Render-props

+

{count}

+ +
+ )} +
+); + +/** + * Main App + */ +const App = () => ( +
+

Simple counter example

+
+ +
+ +
+
+); + +ReactDOM.render(, document.getElementById('root')); diff --git a/package-lock.json b/package-lock.json index af16def..c570454 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1763,6 +1763,12 @@ } } }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -1817,6 +1823,37 @@ "integrity": "sha512-b8bbUOTwzIY3V5vDTY1fIJ+ePKDUBqt2hC2woVGotdQQhG/2Sh62HOKHrT7ab+VerXAcPyAiTEipPu/FsreUtg==", "dev": true }, + "@types/parsimmon": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@types/parsimmon/-/parsimmon-1.10.0.tgz", + "integrity": "sha512-bsTIJFVQv7jnvNiC42ld2pQW2KRI+pAG243L+iATvqzy3X6+NH1obz2itRKDZZ8VVhN3wjwYax/VBGCcXzgTqQ==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.1.tgz", + "integrity": "sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==", + "dev": true + }, + "@types/react": { + "version": "16.8.22", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.22.tgz", + "integrity": "sha512-C3O1yVqk4sUXqWyx0wlys76eQfhrQhiDhDlHBrjER76lR2S2Agiid/KpOU9oCqj1dISStscz7xXz1Cg8+sCQeA==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^2.2.0" + } + }, + "@types/react-dom": { + "version": "16.8.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.8.5.tgz", + "integrity": "sha512-idCEjROZ2cqh29+trmTmZhsBAUNQuYrF92JHKzZ5+aiFM1mlSk3bb23CK7HhYuOY75Apgap5y2jTyHzaM2AJGA==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -1829,6 +1866,71 @@ "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.11.0.tgz", + "integrity": "sha512-mXv9ccCou89C8/4avKHuPB2WkSZyY/XcTQUXd5LFZAcLw1I3mWYVjUu6eS9Ja0QkP/ClolbcW9tb3Ov/pMdcqw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "1.11.0", + "eslint-utils": "^1.3.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^2.0.1", + "tsutils": "^3.7.0" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.11.0.tgz", + "integrity": "sha512-7LbfaqF6B8oa8cp/315zxKk8FFzosRzzhF8Kn/ZRsRsnpm7Qcu25cR/9RnAQo5utZ2KIWVgaALr+ZmcbG47ruw==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "1.11.0", + "eslint-scope": "^4.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.11.0.tgz", + "integrity": "sha512-5xBExyXaxVyczrZvbRKEXvaTUFFq7gIM9BynXukXZE0zF3IQP/FxF4mPmmh3gJ9egafZFqByCpPTFm3dk4SY7Q==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "1.11.0", + "@typescript-eslint/typescript-estree": "1.11.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.11.0.tgz", + "integrity": "sha512-fquUHF5tAx1sM2OeRCC7wVxFd1iMELWMGCzOSmJ3pLzArj9+kRixdlC4d5MncuzXpjEqc6045p3KwM0o/3FuUA==", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + } + } + }, "@webassemblyjs/ast": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", @@ -2418,10 +2520,54 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, "babel-eslint": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.1.tgz", - "integrity": "sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.2.tgz", + "integrity": "sha512-UdsurWPtgiPgpJ06ryUnuaSXC2s0WoSZnQmEpbAH65XZSdwowgN5MvyP7e88nW07FYXv72erVtpBkxyDVKhH1Q==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -2852,7 +2998,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -2889,7 +3035,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -2943,7 +3089,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -3166,7 +3312,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -3187,12 +3334,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3207,17 +3356,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3334,7 +3486,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3346,6 +3499,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3360,6 +3514,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3367,12 +3522,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3391,6 +3548,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3471,7 +3629,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3483,6 +3642,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3568,7 +3728,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3604,6 +3765,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3623,6 +3785,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3666,12 +3829,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -4134,7 +4299,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -4147,7 +4312,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -4230,6 +4395,12 @@ "cssom": "0.3.x" } }, + "csstype": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.5.tgz", + "integrity": "sha512-JsTaiksRsel5n7XwqPAfB0l3TFKdpjW/kgAELf9vrb5adGA7UCPLajKK5s3nFrcFm3Rkyp/Qkgl73ENc1UY3cA==", + "dev": true + }, "cyclist": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", @@ -4400,6 +4571,16 @@ } } }, + "definitelytyped-header-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/definitelytyped-header-parser/-/definitelytyped-header-parser-1.2.0.tgz", + "integrity": "sha512-xpg8uu/2YD/reaVsZV4oJ4g7UDYFqQGWvT1W9Tsj6q4VtWBSaig38Qgah0ZMnQGF9kAsAim08EXDO1nSi0+Nog==", + "dev": true, + "requires": { + "@types/parsimmon": "^1.3.0", + "parsimmon": "^1.2.0" + } + }, "del": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", @@ -4485,6 +4666,12 @@ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", "dev": true }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, "diff-sequences": { "version": "24.3.0", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", @@ -4493,7 +4680,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -4790,6 +4977,66 @@ "is-obj": "^1.0.0" } }, + "download-file-sync": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/download-file-sync/-/download-file-sync-1.0.4.tgz", + "integrity": "sha1-0+PFQ/g29BA5RVuQNMcuNVsDYBk=", + "dev": true + }, + "dts-critic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dts-critic/-/dts-critic-2.0.0.tgz", + "integrity": "sha512-oYo69B8NcjUoDhKh8oUl58ljMsgyWnPR0Qf3QKJmKjm5LvkQ21v4SW+QCOleI1Cs0HRN6zEYllvQEORGd45wOQ==", + "dev": true, + "requires": { + "definitelytyped-header-parser": "^1.2.0", + "download-file-sync": "^1.0.4", + "semver": "^6.2.0", + "yargs": "^12.0.5" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "dtslint": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/dtslint/-/dtslint-0.9.1.tgz", + "integrity": "sha512-IxGanh+u1psQscSHX30cDqV6oQaSs5bhCIRhE4VriRFPOvqOR3vZPq/N6238ft5rS9P64z+oCZ+8kktXc2uCbQ==", + "dev": true, + "requires": { + "definitelytyped-header-parser": "1.2.0", + "dts-critic": "^2.0.0", + "fs-extra": "^6.0.1", + "request": "^2.88.0", + "strip-json-comments": "^2.0.1", + "tslint": "5.14.0", + "typescript": "^3.7.0-dev.20190816" + }, + "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "typescript": { + "version": "3.7.0-dev.20190816", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.0-dev.20190816.tgz", + "integrity": "sha512-h7OoIfjZCR3pfbGHllT/VOwNQzobJPPkzZlnmyihq1Eezww3P+aDUMKro1kBkEEhu8atC+OLXc6vHh++OWrqsw==", + "dev": true + } + } + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -5062,13 +5309,13 @@ } }, "eslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", - "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.0.1.tgz", + "integrity": "sha512-DyQRaMmORQ+JsWShYsSg4OPTjY56u1nCjAmICrE8vLWqyLKxhFXOthwMj1SA8xwfrv0CofLNVnqbfyhwCkaO0w==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", + "ajv": "^6.10.0", "chalk": "^2.1.0", "cross-spawn": "^6.0.5", "debug": "^4.0.1", @@ -5076,18 +5323,19 @@ "eslint-scope": "^4.0.3", "eslint-utils": "^1.3.1", "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", + "espree": "^6.0.0", "esquery": "^1.0.1", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", + "glob-parent": "^3.1.0", "globals": "^11.7.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "inquirer": "^6.2.2", - "js-yaml": "^3.13.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", "lodash": "^4.17.11", @@ -5095,7 +5343,6 @@ "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", "progress": "^2.0.0", "regexpp": "^2.0.1", "semver": "^5.5.1", @@ -5159,6 +5406,15 @@ } } }, + "eslint-config-prettier": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.0.0.tgz", + "integrity": "sha512-vDrcCFE3+2ixNT5H83g28bO/uYAwibJxerXPj+E7op4qzBCsAV36QfvdAyVOoNxKAH2Os/e01T/2x++V0LPukA==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + } + }, "eslint-import-resolver-node": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", @@ -5234,18 +5490,18 @@ } }, "eslint-plugin-flowtype": { - "version": "3.10.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-3.10.3.tgz", - "integrity": "sha512-b1OzI5drhiDmIG52jiZVR7IWQkiwN1vLD+VqvYuLnpzGBwfdw/mjdXz+qN7XN1IVKQ6pUSV0t4F9TxKoJNkpRA==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-3.11.1.tgz", + "integrity": "sha512-4NiaaGZuz9iEGRTK8j4lkA/scibOXSYaYoHbsTtgLOxxqQCkbWV3xt8ETqILKg7DAYDqB69z1H5U71UmtdF9hw==", "dev": true, "requires": { "lodash": "^4.17.11" } }, "eslint-plugin-import": { - "version": "2.17.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.17.3.tgz", - "integrity": "sha512-qeVf/UwXFJbeyLbxuY8RgqDyEKCkqV7YC+E5S5uOjAp4tOc8zj01JP3ucoBM8JcEqd1qRasJSg6LLlisirfy0Q==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.0.tgz", + "integrity": "sha512-PZpAEC4gj/6DEMMoU2Df01C5c50r7zdGIN52Yfi7CvvWaYssG7Jt5R9nFG5gmqodxNOz9vQS87xk6Izdtpdrig==", "dev": true, "requires": { "array-includes": "^3.0.3", @@ -5282,7 +5538,7 @@ }, "load-json-file": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { @@ -5364,17 +5620,34 @@ } } }, + "eslint-plugin-jest": { + "version": "22.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-22.7.1.tgz", + "integrity": "sha512-CrT3AzA738neimv8G8iK2HCkrCwHnAJeeo7k5TEHK86VMItKl6zdJT/tHBDImfnVVAYsVs4Y6BUdBZQCCgfiyw==", + "dev": true + }, + "eslint-plugin-prettier": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz", + "integrity": "sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-plugin-react": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.13.0.tgz", - "integrity": "sha512-uA5LrHylu8lW/eAH3bEQe9YdzpPaFd9yAJTwTi/i/BKTD7j6aQMKVAdGM/ML72zD6womuSK7EiGtMKuK06lWjQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.14.2.tgz", + "integrity": "sha512-jZdnKe3ip7FQOdjxks9XPN0pjUKZYq48OggNMd16Sk+8VXx6JOvXmlElxROCgp7tiUsTsze3jd78s/9AFJP2mA==", "dev": true, "requires": { "array-includes": "^3.0.3", "doctrine": "^2.1.0", "has": "^1.0.3", "jsx-ast-utils": "^2.1.0", + "object.entries": "^1.1.0", "object.fromentries": "^2.0.0", + "object.values": "^1.1.0", "prop-types": "^15.7.2", "resolve": "^1.10.1" }, @@ -5391,9 +5664,9 @@ } }, "eslint-plugin-react-hooks": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.6.0.tgz", - "integrity": "sha512-lHBVRIaz5ibnIgNG07JNiAuBUeKhEf8l4etNx5vfAEwqQ5tcuK3jV9yjmopPgQDagQb7HwIuQVsE3IVcGrRnag==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.6.1.tgz", + "integrity": "sha512-wHhmGJyVuijnYIJXZJHDUF2WM+rJYTjulUTqF9k61d3BTk8etydz+M4dXUVH7M76ZRS85rqBTCx0Es/lLsrjnA==", "dev": true }, "eslint-scope": { @@ -5419,9 +5692,9 @@ "dev": true }, "espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.0.0.tgz", + "integrity": "sha512-lJvCS6YbCn3ImT3yKkPe0+tJ+mH6ljhGNjHQH9mRtiO6gjhVAOhVXW1yjnwqGwTkK3bGbye+hb00nFNmu0l/1Q==", "dev": true, "requires": { "acorn": "^6.0.7", @@ -5900,6 +6173,12 @@ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", "dev": true }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -6108,9 +6387,9 @@ } }, "flatted": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "dev": true }, "flow-bin": { @@ -6501,7 +6780,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -6522,12 +6802,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6542,17 +6824,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -6669,7 +6954,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -6681,6 +6967,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6695,6 +6982,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6702,12 +6990,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -6726,6 +7016,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -6806,7 +7097,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -6818,6 +7110,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -6903,7 +7196,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6939,6 +7233,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6958,6 +7253,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -7001,12 +7297,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -7039,6 +7337,12 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + }, "get-stream": { "version": "3.0.0", "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -7172,7 +7476,7 @@ }, "globby": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { @@ -7529,9 +7833,9 @@ "dev": true }, "import-fresh": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", - "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", + "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -7637,9 +7941,9 @@ "dev": true }, "inquirer": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.3.1.tgz", - "integrity": "sha512-MmL624rfkFt4TG9y/Jvmt8vdmOo836U7Y0Hxr2aFk3RelZEGX4Igk0KabWrcaaZaTv9uzglOqWh1Vly+FAWAXA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.4.1.tgz", + "integrity": "sha512-/Jw+qPZx4EDYsaT6uz7F4GJRNFMRdKNeUZw3ZnKV8lyuUgz/YWRCSUAJMZSVhSq4Ec0R2oYnyi6b3d4JXcL5Nw==", "dev": true, "requires": { "ansi-escapes": "^3.2.0", @@ -8529,7 +8833,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -8550,12 +8855,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8570,17 +8877,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -8697,7 +9007,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -8709,6 +9020,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -8723,6 +9035,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -8730,12 +9043,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -8754,6 +9069,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -8834,7 +9150,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -8846,6 +9163,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -8931,7 +9249,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -8967,6 +9286,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8986,6 +9306,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -9029,12 +9350,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -9540,12 +9863,13 @@ } }, "jsx-ast-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.1.0.tgz", - "integrity": "sha512-yDGDG2DS4JcqhA6blsuYbtsT09xL8AoLuUR2Gb5exrw7UEM19sBcOTq+YBBhrNbl0PUC4R4LnFu+dHg2HKeVvA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz", + "integrity": "sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ==", "dev": true, "requires": { - "array-includes": "^3.0.3" + "array-includes": "^3.0.3", + "object.assign": "^4.1.0" } }, "kefir": { @@ -9850,6 +10174,12 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", + "dev": true + }, "loglevel": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.3.tgz", @@ -11019,6 +11349,12 @@ "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", "dev": true }, + "parsimmon": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/parsimmon/-/parsimmon-1.13.0.tgz", + "integrity": "sha512-5UIrOCW+gjbILkjKPgTgmq8LKf8TT3Iy7kN2VD7OtQ81facKn8B4gG1X94jWqXYZsxG2KbJhrv/Yq/5H6BQn7A==", + "dev": true + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -11195,6 +11531,15 @@ "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==", "dev": true }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-format": { "version": "24.8.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.8.0.tgz", @@ -12282,7 +12627,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -12895,9 +13240,9 @@ "dev": true }, "table": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.0.tgz", - "integrity": "sha512-nHFDrxmbrkU7JAFKqKbDJXfzrX2UBsWmrieXFTGxiI5e4ncg3VqsZeI4EzNmX0ncp4XNGVeoxIWJXfCIXwrsvw==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.1.tgz", + "integrity": "sha512-E6CK1/pZe2N75rGZQotFOdmzWQ1AILtgYbMAbAjvms0S1l5IDB47zG3nCnFGB/w+7nB3vKofbLXCH7HPBo864w==", "dev": true, "requires": { "ajv": "^6.9.1", @@ -13295,6 +13640,47 @@ "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", "dev": true }, + "tslint": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.14.0.tgz", + "integrity": "sha512-IUla/ieHVnB8Le7LdQFRGlVJid2T/gaJe5VkjzRVSRR6pA2ODYrnfR1hmxi+5+au9l50jBwpbBL34txgv4NnTQ==", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.29.0" + }, + "dependencies": { + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "tsutils": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.14.0.tgz", + "integrity": "sha512-SmzGbB0l+8I0QwsPgjooFRaRvHLBLNYM8SeQ0k6rtNDru5sCGeLJcZdwilNndN+GysuFjF5EIYgN8GfFG6UeUw==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", @@ -13364,6 +13750,12 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typescript": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz", + "integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==", + "dev": true + }, "uglify-js": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", diff --git a/package.json b/package.json index 305d1ee..c52d729 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "state management", "Redux" ], + "types": "./types/index.d.ts", "main": "lib/cjs/index.js", "module": "lib/esm/index.js", "repository": { @@ -27,9 +28,9 @@ "build:flow": "echo lib/cjs lib/esm | xargs -n 1 cp src/index.js.flow", "build": "npm run clean:build -s && npm run build:cjs -s && npm run build:esm -s && npm run build:flow -s", "test": "jest", - "flow": "flow --max-warnings=0", + "types": "dtslint --expectOnly --localTs node_modules/typescript/lib ./types && tsc && flow --max-warnings=0", "lint": "eslint ./src", - "preversion": "npm run lint -s && npm run flow -s && npm run test -s", + "preversion": "npm run lint -s && npm run types -s && npm run test -s", "prepack": "npm run preversion -s && npm run build -s", "docs": "docsify serve docs", "start": "webpack-dev-server", @@ -53,17 +54,25 @@ "@babel/preset-flow": "^7.0.0", "@babel/preset-react": "^7.0.0", "@babel/runtime": "^7.4.5", - "babel-eslint": "^10.0.1", + "@types/react": "^16.8.20", + "@types/react-dom": "^16.8.5", + "@typescript-eslint/eslint-plugin": "^1.11.0", + "@typescript-eslint/parser": "^1.11.0", + "babel-eslint": "^10.0.2", "babel-jest": "^24.8.0", "babel-loader": "^8.0.6", "docsify-cli": "^4.3.0", + "dtslint": "^0.9.1", "enzyme": "^3.10.0", "enzyme-adapter-react-16": "^1.14.0", - "eslint": "^5.16.0", - "eslint-plugin-flowtype": "^3.10.3", - "eslint-plugin-import": "^2.17.3", - "eslint-plugin-react": "^7.13.0", - "eslint-plugin-react-hooks": "^1.6.0", + "eslint": "^6.0.1", + "eslint-config-prettier": "^6.0.0", + "eslint-plugin-flowtype": "^3.11.1", + "eslint-plugin-import": "^2.18.0", + "eslint-plugin-jest": "^22.7.1", + "eslint-plugin-prettier": "^3.1.0", + "eslint-plugin-react": "^7.14.2", + "eslint-plugin-react-hooks": "^1.6.1", "flow-bin": "^0.97.0", "flow-copy-source": "^2.0.6", "jest": "^24.8.0", @@ -71,6 +80,7 @@ "prop-types": "~15.7.2", "react": "^16.8.6", "react-dom": "^16.8.6", + "typescript": "^3.5.2", "webpack": "^4.34.0", "webpack-cli": "^3.3.4", "webpack-dev-server": "^3.7.1" diff --git a/src/__tests__/flow.js b/src/__tests__/types.flow.js similarity index 93% rename from src/__tests__/flow.js rename to src/__tests__/types.flow.js index c580b48..6cab0c0 100644 --- a/src/__tests__/flow.js +++ b/src/__tests__/types.flow.js @@ -14,6 +14,7 @@ import { */ type State = {| count: number |}; type Actions = typeof actions; +type SelectorProps = {| min: number |}; let Test; let TypeStore; @@ -22,8 +23,6 @@ let TypeSubscriber; let typeHook; let TypeSelector; -type SelectorProps = {| min: number |}; - const actions = { // setState tests increment: (n: number): Action => ({ setState }) => { @@ -196,6 +195,7 @@ Test[1].increment(); // $ExpectError Array index 1 should be actions Test[1].increment('1'); +// TODO Test[1].decrement().then(v => v); // Correct @@ -255,7 +255,7 @@ Test[0].min + Test[0].baz; /** * Container types tests */ -TypeContainer = createContainer(TypeStore); +TypeContainer = createContainer(TypeStore); Test = ( // $ExpectError Container is not a render-prop @@ -263,7 +263,7 @@ Test = ( ); Test = ( - // $ExpectError Only allows typed extra props + // $ExpectError Does not accept extra props bla ); @@ -271,8 +271,21 @@ Test = ( Test = bla; Test = bla; Test = bla; + +const TypePropsContainer = createContainer( + TypeStore +); + +// $ExpectError Requires typed props +Test = bla; + +Test = ( + // $ExpectError Only allows typed extra props + bla +); + Test = ( - + bla - + ); diff --git a/src/index.js.flow b/src/index.js.flow index c12bf22..1479c97 100644 --- a/src/index.js.flow +++ b/src/index.js.flow @@ -29,7 +29,7 @@ export type Store = {| export type StoreState = {| getState: GetState, - setState: ST => void, + setState: SetState, key: string[], subscribe: (listener: () => void) => StoreUnsubscribe, mutator: SetState, @@ -97,12 +97,20 @@ export type SubscriberComponent = React$ComponentType<{| ...PR, |}>; +/** + * createStore + */ + declare export function createStore({| initialState: ST, actions: AC, name?: string, |}): Store; +/** + * createContainer + */ + declare export function createContainer( store: Store, options?: {| diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..574e6fd --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "es2015", + "esModuleInterop": true, + "moduleResolution": "node", + "strict": true, + "preserveConstEnums": true, + "jsx": "preserve", + "noEmit": true, + "typeRoots" : ["./node_modules/@types", "types"], + "baseUrl": ".", + "paths": { + "react-sweet-state": ["types"] + } + }, + "include": ["examples/**/*"], +} diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 0000000..217a5ef --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,201 @@ +declare module 'react-sweet-state' { + import { ComponentType, ReactNode, ReactElement } from 'react'; + + type SetState = (newState: Partial) => void; + type GetState = () => Readonly; + type StoreUnsubscribe = () => void; + + type RenderPropComponent = ( + state: TState, + actions: TActions + ) => ReactNode; + + type HookReturnValue< + TState, + TActions extends Record> + > = [TState, BoundActions]; + + type ActionThunk< + TState, + TActions extends Record> + > = (...args: any[]) => ActionAny; + + type Store< + TState, + TActions extends Record> + > = { + key: string[]; + initialState: TState; + actions: TActions; + }; + + type StoreState = { + getState: GetState; + setState: SetState; + key: string[]; + subscribe: (listener: () => void) => StoreUnsubscribe; + mutator: SetState; + }; + + type ActionApi = { + setState: SetState; + getState: GetState; + dispatch: >( + actionThunk: T + ) => ReturnType; + }; + + type ActionAny = ( + api: ActionApi, + containerProps: TContainerProps + ) => any; + + type BoundActions< + TState, + TActions extends Record> + > = { + [K in keyof TActions]: ( + ...args: Parameters + ) => ReturnType>; + }; + + interface StoreInstance { + store: StoreState; + actions: TActions; + } + + class Registry { + configure(options: { initialStates?: { [key: string]: any } }): void; + stores: Map>; + initStore: < + TState, + TActions extends Record> + >( + store: Store, + key: string + ) => StoreInstance; + getStore: < + TState, + TActions extends Record> + >( + store: Store, + scopeId: string + ) => StoreInstance; + deleteStore: < + TState, + TActions extends Record> + >( + store: Store, + scopeId: string + ) => void; + } + + const defaultRegistry: Registry; + + type MiddlewareResult = any; + type Middleware = ( + store: StoreState + ) => ( + next: (fn: any) => MiddlewareResult + ) => (fn: () => any) => MiddlewareResult; + + const defaults: { + devtools: boolean; + middlewares: any; + mutator: ( + prevState: TState, + partialState: Partial + ) => TState; + }; + + type ContainerComponent = ComponentType< + { + scope?: string; + isGlobal?: boolean; + } & TProps + >; + + type SubscriberComponent< + TState, + TActions, + TProps = undefined + > = ComponentType< + { + children: RenderPropComponent; + } & TProps + >; + + /** + * createStore + */ + + function createStore< + TState, + TActions extends Record> + >(config: { + initialState: TState; + actions: TActions; + name?: string; + }): Store; + + /** + * createContainer + */ + + function createContainer< + TState, + TActions extends Record>, + TProps = {} + >( + store: Store, + options?: { + onInit?: () => ActionAny; + onUpdate?: () => ActionAny; + displayName?: string; + } + ): ContainerComponent; + + /** + * createSubscriber + */ + + type Selector = ( + state: TState, + props: TProps + ) => TOutput; + + function createSubscriber< + TState, + TActions extends Record>, + TSelectedState = TState, + TProps = {} + >( + store: Store, + options?: { + displayName?: string; + selector?: Selector | null; + } + ): SubscriberComponent< + TSelectedState, + BoundActions, + TProps + >; + + /** + * createHook + */ + + function createHook< + TState, + TActions extends Record>, + TSelectedState = TState, + TArg = void + >( + store: Store, + options?: { + selector?: Selector | null; + } + ): ( + ...args: TArg extends undefined ? [] : [TArg] + ) => [TSelectedState, BoundActions]; +} diff --git a/types/test.tsx b/types/test.tsx new file mode 100644 index 0000000..a892521 --- /dev/null +++ b/types/test.tsx @@ -0,0 +1,302 @@ +import React from 'react'; + +import { + createStore, + createContainer, + createSubscriber, + createHook, + ActionApi, +} from 'react-sweet-state'; + +/** + * Store types tests + */ +type State = { count: number }; +type Actions = typeof actions; +type SelectorProps = { min: number }; + +let Test; + +const actions = { + // setState tests + increment: (n: number) => ({ setState }: ActionApi) => { + // $ExpectError + setState(''); + + // $ExpectError + setState({ foo: 1 }); + + // Correct + setState({ + count: 2, + }); + + return ''; + }, + + // GetState tests + decrement: () => ({ setState, getState }: ActionApi) => { + const state = getState(); + // $ExpectError + const bla = state.bla; + // $ExpectError + state.count = 1; + + // correct + const { count } = state; + + return count; + }, + + fetch: () => async (): Promise => { + return ''; + }, + + // Dispatch tests + setTitle: (title: string) => ({ dispatch }: ActionApi) => { + const v0 = dispatch(actions.decrement()); + // $ExpectError + dispatch(actions.decrement()).then(); + // $ExpectError + v0.split(''); + + // Correct + dispatch(actions.decrement()); + dispatch(actions.fetch()).then(v => v + 1); + dispatch(actions.fetch()).then(v => v.split('')); + v0 + 1; + return title; + }, +}; + +// $ExpectError +const TypeStore0 = createStore({ count: 0 }); + +const TypeStore1 = createStore({ + // $ExpectError + initialState: { bla: 0 }, + actions, +}); + +// $ExpectError +const TypeStore2 = createStore({ initialState: { count: 0 } }); + +// Correct +const TypeStore = createStore({ + initialState: { count: 0 }, + actions, + name: 'Type', +}); + +/** + * createSubscriber types tests + */ + +const TypeSubscriber = createSubscriber(TypeStore); + +Test = ( + // $ExpectError + {({ foo }) => foo} +); + +Test = ( + // $ExpectError + {(__, { increment }) => increment()} +); + +// Correct +Test = {({ count }) => count + 0}; +Test = {(__, { increment }) => increment(1)}; + +/** + * createSubscriber with selector types tests + */ +const TypeSelector = createSubscriber( + TypeStore, + { + selector: state => ({ baz: 1 }), + } +); + +Test = ( + // $ExpectError + {({ count }) => count} +); + +Test = ( + // $ExpectError + {({ baz }) => baz} +); + +// Correct +Test = {({ baz }) => baz}; +Test = {(__, { increment }) => increment(1)}; + +const TypeSelectorNull = createSubscriber(TypeStore, { + selector: null, +}); + +Test = ( + // $ExpectError + {({ count }) => count} +); + +Test = ( + // $ExpectError + {state => state === undefined} +); + +// Correct +Test = ( + {(state, { increment }) => increment(1)} +); + +type SelectorState = { baz: number; min: number }; + +const TypeSelectorProp = createSubscriber< + State, + Actions, + SelectorState, + SelectorProps +>(TypeStore, { + selector: (state, props) => ({ baz: 1, min: props.min }), +}); + +Test = ( + // $ExpectError + {({ baz }) => baz} +); + +Test = ( + // $ExpectError + {({ baz }) => baz} +); + +Test = ( + // $ExpectError + {({ min }) => min.split('')} +); + +// Correct +Test = ( + {({ baz, min }) => baz + min} +); + +/** + * createHook types tests + */ + +const typeBaseHook = createHook(TypeStore); + +const baseReturn = typeBaseHook(); + +// $ExpectError +typeBaseHook({}); + +// $ExpectError +baseReturn[0].foo; + +// $ExpectError +baseReturn[1].increment(); + +// $ExpectError +baseReturn[1].increment('1'); + +// $ExpectError +baseReturn[1].decrement().then(v => v); + +// Correct +baseReturn[0].count + 0; +baseReturn[1].increment(1); +baseReturn[1].decrement(); +baseReturn[1].fetch().then(v => v); + +/** + * createHook with selector types tests + */ +const typeSelectorHook = createHook( + TypeStore, + { selector: state => ({ baz: 1 }) } +); + +const selectorReturn = typeSelectorHook(); + +// $ExpectError +selectorReturn[0].count; + +// $ExpectError +typeSelectorHook({ min: 3 }); + +// Correct +selectorReturn[0].baz; +selectorReturn[1].increment(1); + +const typeNullHook = createHook(TypeStore, { + selector: null, +}); + +const nullReturn = typeNullHook(); + +// $ExpectError +nullReturn[0].count; + +// Correct +nullReturn[1].increment(1); + +const typeArgHook = createHook( + TypeStore, + { selector: (state, props) => ({ baz: 1, min: props.min }) } +); + +// $ExpectError +typeArgHook(); + +// $ExpectError +typeArgHook({ min: '2' }); + +const argReturn = typeArgHook({ min: 2 }); +// $ExpectError +argReturn[0].min.split(''); + +// Correct +argReturn[0].min + argReturn[0].baz; + +/** + * Container types tests + */ + +const TypeContainer = createContainer(TypeStore); + +Test = ( + // $ExpectError + {({ count }) => count} +); + +Test = ( + // $ExpectError + bla +); + +// Correct +Test = bla; +Test = bla; +Test = bla; + +const TypePropsContainer = createContainer( + TypeStore +); + +// $ExpectError +Test = bla; + +Test = ( + // $ExpectError + bla +); + +// Correct +Test = ( + + bla + +); diff --git a/types/tsconfig.json b/types/tsconfig.json new file mode 100644 index 0000000..a38aca0 --- /dev/null +++ b/types/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "noUnusedLocals": false, + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "lib": ["es2015"] + }, + "include": ["*"] +}