diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml new file mode 100644 index 00000000..fe6b2664 --- /dev/null +++ b/.github/workflows/pipeline.yml @@ -0,0 +1,147 @@ +name: re-formality pipeline + +on: + pull_request: + branches: + - master + +jobs: + build: + name: Build on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + node-version: [12.x] + os: [ubuntu-latest, macOS-latest, windows-latest] + + steps: + - name: Checkout repo + uses: actions/checkout@v2 + + - name: Setup Node ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - name: Print Yarn cache + id: print-yarn-cache + run: echo "::set-output name=yarn-cache::$(yarn cache dir)" + + - name: Restore Yarn cache + id: yarn-cache + uses: actions/cache@v1 + with: + path: ${{ steps.print-yarn-cache.outputs.yarn-cache }} + key: ${{ matrix.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + + - name: Install Yarn deps + run: | + yarn install + + - name: Install Esy + run: | + npm install -g esy@0.6.2 + + - name: Install Esy deps + run: | + cd lib + esy install + + - name: Print Esy cache + id: print-esy-cache + run: node .github/workflows/scripts/print-esy-cache.js + + - name: Restore Esy cache + id: esy-cache + uses: actions/cache@v1 + with: + path: ${{ steps.print-esy-cache.outputs.esy-cache }} + key: ${{ matrix.os }}-esy-${{ hashFiles('**/index.json') }} + + - name: Build ppx + run: | + cd lib + esy build + + - name: Build BuckleScript lib + run: | + cd lib + yarn bs:build + + - name: Install public interface of BuckleScript lib + run: | + cd lib + yarn bs:install + + - name: Run ppx tests + # FIXME: Snapshot tests are broken on Win + if: matrix.os != 'windows-latest' + run: | + cd lib + esy x test.exe + + - name: Run integration tests + run: | + cd specs + yarn run test + + - name: Upload artifacts + uses: actions/upload-artifact@v1 + with: + name: ${{ matrix.os }} + path: lib/_build/default/bin/bin.exe + + rc: + needs: build + name: Prepare RC + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v2 + + - name: Setup Node ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: 12.x + + - name: Download Linux artifacts + uses: actions/download-artifact@v1 + with: + name: ubuntu-latest + path: _bin/linux + + - name: Download macOS artifacts + uses: actions/download-artifact@v1 + with: + name: macOS-latest + path: _bin/darwin + + - name: Download Windows artifacts + uses: actions/download-artifact@v1 + with: + name: windows-latest + path: _bin/windows + + - name: Move artifacts + run: | + mkdir -p _release/bin + mv _bin/darwin/bin.exe _release/bin/re-formality-ppx-darwin-x64.exe + mv _bin/windows/bin.exe _release/bin/re-formality-ppx-win-x64.exe + mv _bin/linux/bin.exe _release/bin/re-formality-ppx-linux-x64.exe + rm -rf _bin + + - name: Move lib files + run: | + mkdir -p _release/src + cp README.md _release/README.md + cp lib/bsconfig.json _release/bsconfig.json + cp -a lib/src/. _release/src/ + cp .github/workflows/scripts/copy-binaries.js _release/postinstall.js + node .github/workflows/scripts/write-package-json.js + + - name: Upload release + uses: actions/upload-artifact@v1 + with: + name: release + path: _release diff --git a/.github/workflows/scripts/copy-binaries.js b/.github/workflows/scripts/copy-binaries.js new file mode 100644 index 00000000..547bb446 --- /dev/null +++ b/.github/workflows/scripts/copy-binaries.js @@ -0,0 +1,53 @@ +#!/usr/bin/env node + +const fs = require("fs"); + +const PPX = "re-formality-ppx"; + +let arch = process.arch; +let platform = process.platform; + +if (arch === "ia32") { + arch = "x86"; +} + +if (platform === "win32") { + platform = "win"; +} + +const filename = `bin/${PPX}-${platform}-${arch}.exe`; + +const supported = fs.existsSync(filename); + +if (!supported) { + console.error(`${PPX} does not support this platform :(`); + console.error(""); + console.error(`${PPX} comes prepacked as built binaries to avoid large`); + console.error("dependencies at build-time."); + console.error(""); + console.error(`If you want ${PPX} to support this platform natively,`); + console.error("please open an issue at our repository, linked above. Please"); + console.error(`specify that you are on the ${platform} platform,`); + console.error(`on the ${arch} architecture.`); + +} + +if (platform === "win") { + if (!fs.existsSync("ppx.exe")) { + copyFileSync(filename, "ppx.exe"); + fs.chmodSync("ppx.exe", 0755); + } +} else { + if (!fs.existsSync("ppx")) { + copyFileSync(filename, "ppx"); + fs.chmodSync("ppx", 0755); + } +} + +function copyFileSync(source, dest) { + if (typeof fs.copyFileSync === "function") { + fs.copyFileSync(source, dest); + } else { + fs.writeFileSync(dest, fs.readFileSync(source)); + } +} diff --git a/.github/workflows/scripts/print-esy-cache.js b/.github/workflows/scripts/print-esy-cache.js new file mode 100644 index 00000000..4d417a38 --- /dev/null +++ b/.github/workflows/scripts/print-esy-cache.js @@ -0,0 +1,15 @@ +const fs = require("fs"); +const os = require("os"); +const path = require("path"); + +const ESY_FOLDER = process.env.ESY__PREFIX + ? process.env.ESY__PREFIX + : path.join(os.homedir(), ".esy"); + +const esy3 = fs + .readdirSync(ESY_FOLDER) + .filter(name => name.length > 0 && name[0] === "3") + .sort() + .pop(); + +console.log(`::set-output name=esy-cache::${path.join(ESY_FOLDER, esy3, "i")}`); diff --git a/.github/workflows/scripts/write-package-json.js b/.github/workflows/scripts/write-package-json.js new file mode 100644 index 00000000..e2b017b3 --- /dev/null +++ b/.github/workflows/scripts/write-package-json.js @@ -0,0 +1,43 @@ +const fs = require("fs"); +const path = require("path"); + +const { + name, + version, + description, + author, + license, + repository, + keywords +} = require("../../../package.json"); +const { dependencies } = require("../../../lib/package.json"); + +const packageJson = JSON.stringify( + { + name, + version, + description, + author, + license, + repository, + keywords, + dependencies, + files: [ + "src", + "bin", + "bsconfig.json", + "postinstall.js", + ], + scripts: { + postinstall: "node ./postinstall.js" + } + }, + null, + 2 +); + +fs.writeFileSync( + path.join(__dirname, "..", "..", "..", "_release", "package.json"), + packageJson, + { encoding: "utf8" } +); diff --git a/.gitignore b/.gitignore index 7d4d6da1..b003e0b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,13 @@ node_modules/ -lib/ -dist/ +*/**/lib/ +*/dist/ .merlin .bsb.lock *.bs.js yarn-error.log .cache .DS_Store +_esy +_build +re-formality-ppx.install +lib/test/**/*.cm[itj] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6a15b0ce..00000000 --- a/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: node_js - -node_js: - - "8" - -script: - - yarn run test - - yarn run build - -cache: - yarn: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..85c5ee60 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,71 @@ +# Contributing + +## Requesting a feature +If you would like to request a feature, please, [create an issue](https://github.com/MinimaHQ/re-formality/issues/new). + +## Reporting a bug +If you want to report a bug, there are few options: +1. You can contribute a minimal falling test with your use case. It would make things easier for us to proceed with the fix. +2. You can contribute a minimal falling test and a fix. Even better :) +3. If you don't feel like contributing a test, please, [create an issue](https://github.com/MinimaHQ/re-formality/issues/new) with as many details as possible and we will try to figure out something. + +### Contributing a test +There might be 2 types of issues: +1. Compile-time error. +2. Runtime error (related to logic or just runtime crashes). + +If you are facing the former, add PPX test in [`lib/test`](./lib/test).
+If you are facing the latter, add integration test in [`specs`](./specs). + +See the corresponding README for details. + +It would be great if you could reduce your test case to minimal size. I.e. instead of copy/pasting code from your app as is, try to remove unrelated parts and keep only what's related to the error. + +## Technical details +### Repository structure +```shell +- docs/ # Documentation +- examples/ # Examples +- lib/ # Library + - bin/ # PPX binary + - ppx/ # PPX sources + - sandbox/ # Sandbox for PPX debugging + - src/ # BuckleScript lib sources + - test/ # PPX tests +- specs/ # Integration tests +``` + +### Setup +This repo uses `yarn` workspaces to manage frontend related dependencies and `esy` to manage PPX related dependencies. + +Install dependencies: + +```shell +# Install yarn deps +yarn install + +# Install esy deps +cd lib +esy install +``` + +Build PPX: + +```shell +# In lib folder +esy build +``` + +Build BuckleScript library: + +```shell +# In lib folder +yarn bsb -clean-world -make-world +``` + +Build public interface of the BuckleScript lib: + +```shell +# In lib folder +yarn bsb -install +``` diff --git a/README.md b/README.md index 607d28b7..7ba8c567 100644 --- a/README.md +++ b/README.md @@ -1,701 +1,37 @@ # Formality [![npm version](https://img.shields.io/npm/v/re-formality.svg?style=flat-square)](https://www.npmjs.com/package/re-formality) -[![build status](https://img.shields.io/travis/alexfedoseev/re-formality/master.svg?style=flat-square)](https://travis-ci.org/alexfedoseev/re-formality) +[![build status](https://github.com/MinimaHQ/re-formality/workflows/re-formality%20pipeline/badge.svg)](https://github.com/MinimaHQ/re-formality/actions) [![license](https://img.shields.io/npm/l/re-formality.svg?style=flat-square)](https://www.npmjs.com/package/re-formality) Form validation tool for [`reason-react`](https://reasonml.github.io/reason-react/). ## Features * Validation strategies -* Async validations (debounced on change / on blur) +* Dependent fields +* Collections +* Async validations * I18n compatible -## TOC -- [Installation](#installation) -- [Examples](#examples) -- [Concepts](#concepts) - - [Strategies](#strategies) -- [Usage](#usage) - - [Form config](#form-config) - - [Form hook](#form-hook) - - [Rendering](#rendering) - - [Async validations](#async-validations) - - [Custom field comparator](#custom-field-comparator) - - [I18n](#i18n) -- [Alternatives](#alternatives) -- [License](#license) - -## Installation - -```shell -# yarn -yarn add re-formality -# or npm -npm install --save re-formality -``` - -Then add it to `bsconfig.json`: - -```json -"bs-dependencies": [ - "re-formality" -] -``` +## Documentation +- [Installation](./docs/01-Installation.md) +- [Validation Strategies](./docs/02-ValidationStrategies.md) +- [IO](./docs/03-IO.md) +- [Basic Usage](./docs/04-BasicUsage.md) +- [Async Validation](./docs/05-AsyncValidation.md) +- [Collections](./docs/06-Collections.md) +- [Dependent Fields](./docs/07-DependentFields.md) +- [Form Submission](./docs/08-FormSubmission.md) +- [I18n](./docs/09-I18n.md) +- [Caveats](./docs/10-Caveats.md) +- [API](./docs/11-API.md) ## Examples * [Live demo](https://re-formality.now.sh) * [Live @ minima.app](https://minima.app) * [Sources](./examples) -## Concepts -The main purpose of this library is to provide great form validation UX. To achieve this, `Formality` follows the following principle: - -

-Validation feedback should be provided as soon as possible but not too soon. -

- -The hardest part is to figure out the right moment when first validation results should be emitted in UI. - -Let's break down a case with credit card field. A user opens a form and focuses on a field. When the first character is typed, the field is in an invalid state but it's not polite to show an error immediately: we should let user a chance to finish what he's doing. While the user is typing and field is in invalid state, we don't provide any feedback in UI. If after some character validator reported valid result, it's a proper moment to indicate a success (e.g. show credit card type). But if the user left the field in an invalid state (e.g. moved to another field) we have all rights to emit an error. After the first result is emitted, we update validation state in UI on every change. - -Sadly, form fields are different and credit card scenario is not universal. This is where strategies kick in. - -### Strategies -We can't have a single scenario for all the cases but we can spot the most common ones, describe a logic of each and apply proper scenarios to specific form fields. To understand a behavior of each strategy, add the following prefix to its name: _"Start providing feedback in UI on..."_ - -```reason -module Strategy = { - type t = - | OnFirstBlur - | OnFirstChange - | OnFirstSuccess - | OnFirstSuccessOrFirstBlur - | OnSubmit; -}; -``` - -#### `OnFirstBlur` -Results are emitted on the first blur. After first results are emitted, a user receives feedback on every change in this field. - -#### `OnFirstChange` -Results are emitted on the first change in a field (basically, as a user types). - -#### `OnFirstSuccess` -Results are emitted on first successful validation. After first results are emitted, a user receives feedback on every change in this field. - -#### `OnFirstSuccessOrFirstBlur` ✨ -Results are emitted on first successful validation or on the first blur. After first results are emitted, a user receives feedback on every change in this field. - -#### `OnSubmit` -Results are emitted only after the first submission attempt. After this, results for each field are emitted on every change until the form is reset. - - -## Usage -It takes 3 steps to implement a form: - -1. Define form config. -2. Create form hook. -3. Render form hook and form UI. - -> Code > 1000 words. Quick example for you: - -
-Spoiler - -```reason -module LoginForm = { - open Formality; - - type field = - | Email - | Password; - - type state = { - email: string, - password: string, - }; - - type message = string; - type submissionError = unit; - - module EmailField = { - let update = (state, value) => {...state, email: value}; - - let validator = { - field: Email, - strategy: Strategy.OnFirstSuccessOrFirstBlur, - dependents: None, - validate: state => - switch (state.email) { - | "" => Error("Uh oh error") - | _ => Ok(Valid) - }, - }; - }; - - module PasswordField = { - let update = (state, value) => {...state, password: value}; - - let validator = { - field: Password, - strategy: Strategy.OnFirstBlur, - dependents: None, - validate: state => - switch (state.password) { - | "" => Error("Uh oh error") - | _ => Ok(Valid) - }, - }; - }; - - let validators = [ - EmailField.validator, - PasswordField.validator, - ]; -}; - -module LoginFormHook = Formality.Make(LoginForm); - -[@react.component] -let make = () => { - let form = - LoginFormHook.useForm( - ~initialState=LoginForm.{email: "", password: ""}, - ~onSubmit=(state, form) => { - // Submit form and use callbacks to update form container - }, - ); - -
Formality.Dom.preventDefault}> - form.blur(Email)} - onChange={event => - form.change( - Email, - LoginForm.EmailField.update( - form.state, - event->ReactEvent.Form.target##value, - ), - ) - } - /> - {switch (Email->(form.result)) { - | Some(Error(message)) => -
message->React.string
- | Some(Ok(Valid | NoValue)) - | None => React.null - }} - form.blur(Password)} - onChange={event => - form.change( - Password, - LoginForm.PasswordField.update( - form.state, - event->ReactEvent.Form.target##value, - ), - ) - } - /> - {switch (Password->(form.result)) { - | Some(Error(message)) => -
message->React.string
- | Some(Ok(Valid | NoValue)) - | None => React.null - }} - -
; -}; -``` -
- -### Form config -Form config is a module: - -```reason -module MyForm = { - type field; - type state; - type message; - type submissionError; - let validators: list(validator); -}; -``` - -To make things happen, you must provide few types and list of validators. Depending on whether you need async validations or not, your config will require (or not) additional data. But most of the things are common for all types of forms. Let's start with the simplest case without async validations—this is what's required for all types of forms—and then async differences will be outlined in [Async validations](#async-validations) section. - -#### `type field` -A variant where tags are form fields. - -```reason -type field = - | Email - | Password; -``` - -#### `type state` -`state` is a record that defines a shape of a form state. - -```reason -type state = { - email: string, - password: string, -}; -``` - -#### `type message` -The type of error messages that will be rendered in UI. Feel free to set it to whatever you need. - -The most common scenario is: - -```reason -type message = string; -``` - -If you build i18n'ized app then it's going to be something like this: - -```reason -type message = I18n.t; -``` - -#### `type submissionError` -When you submit a form submission might fail, for various reasons. It might be a bad password on login attempt (expected error) or server crash (unexpected error), anything. This kind of error is specific to a form and its type describes what might go wrong on form submission. - -```reason -type submissionError = - | UserNotFound - | BadPassword - | UnexpectedServerError; -``` - -Later on, on failed form submission, you will be able to pass this error to form container and provide appropriate feedback to users in UI. - -#### `let validators: list(validator)` -Field validators. - -```reason -module EmailField = { - let validator = { - field: Email, - strategy: Strategy.OnFirstSuccessOrFirstBlur, - dependents: None, - validate: state => - switch (state.email) { - | "" => Error("Uh oh error") - | _ => Ok(Valid) - }, - }; -}; - -let validators = [ - EmailField.validator, - ... -]; -``` - -##### `validator` -It's a record of 4 items: - -```reason -type validator('field, 'state, 'message) = { - field: 'field, - strategy: Formality.Strategy.t, - dependents: option(list('field)), - validate: 'state => Result.t(ok, 'message), -}; -``` - -###### `strategy` -See [Strategies](#strategies). - -###### `dependents` -Optional list of fields that must be revalidated on a change in the current field. E.g. `PasswordConfirmation` must be revalidated on a change in `Password` field: - -```reason -field: Password, -dependents: [PasswordConfirmation]->Some -``` - -###### `validate` -A function that takes `state` and returns Belt's `Result.t`: - -```reason -type ok = - | Valid - | NoValue; - -type validate('state, 'message) = 'state => Result.t(ok, 'message); -``` - -Most of the time you need `Ok(Valid)` or `Error('message)`. You want to return `Ok(NoValue)` when optional field receives no value (e.g. `value == ""`). `Valid` and `NoValue` are explicitly differentiated since there's no reason to show success message/icon in UI when no value is provided. - -### Form hook -To create form hook simply do the following: - -```reason -module MyFormHook = Formality.Make(MyForm); -``` - -It creates React `useForm` hook for general form. - -If you render forms with async validations, use: - -```reason -/* Async validations on change (debounced) */ -module MyAsyncFormHook = Formality.Async.Make(MyForm); - -/* Async validations on blur */ -module MyAsyncFormHook = Formality.Async.MakeOnBlur(MyForm); -``` - -### Rendering -Form hook accepts 2 arguments and returns record with everything you need to render your UI: - -```reason -let form = - MyFormHook.useForm( - ~initialState={email: "", password: ""}, - ~onSubmit=(state, form) => { - // Submit form and use callbacks to update form container - }, - ); - -// Use `form` to render your UI... -``` - -#### `initialState` -It's `state` record with initial values for each form field. - -#### `onSubmit` -This handler will be triggered on form submission (only when all validators returned `Ok(_)`). - -It accepts two arguments: -1. `state`: current state of a form -2. `submissionCallbacks`: record with 4 callbacks - -```reason -type submissionCallbacks('state, 'submissionError) = { - notifyOnSuccess: option('state) => unit, - notifyOnFailure: 'submissionError => unit, - reset: unit => unit, - dismissSubmissionResult: unit => unit, -}; -``` - -##### `notifyOnSuccess` -Trigger this callback when server responded with success. It accepts optional state argument: if it's provided, this state will be set as a next form state. - -##### `notifyOnFailure` -Trigger this callback when server responded with an error. It accepts 1 argument of type `MyForm.submissionError` (defined in form config). - -You can access this data in render via `form.status` (see [`form.status`](#form-status)). - -##### `reset` -Simply, resets a form container state. - -##### `form.dismissSubmissionResult` -Use it when you want to dismiss alerts with errors from server or success message without resetting a form. See [`form.status`](#form-status) for more details. - -#### `form` record -`form` record, returned from the hook, contains everything you need to render UI: - -```reason -type form = { - state: Form.state, - status: FormStatus.t, - result: Form.field => option(result), - dirty: unit => bool, - valid: unit => bool, /* not available in async forms */ - submitting: bool, - change: (Form.field, Form.state) => unit, - blur: Form.field => unit, - submit: unit => unit, - reset: unit => unit, - mapSubmissionError: ('submissionError => 'submissionError) => unit, - dismissSubmissionError: unit => unit, - dismissSubmissionResult: unit => unit, -}; -``` - -##### `form.state` -Form state, obviously. Use it to set `values` of the form fields. - -##### `form.status` -Form status is a variant: - -```reason -module FormStatus = { - type t('error) = - | Editing - | Submitting - | Submitted - | SubmissionFailed('error); -}; -``` - -You can use it to show a spinner while a form is `Submitting`, or success message on `Submitted`, or display server errors using data that you passed to `notifyOnFailure` callback (it's available in `SubmissionFailed` tag payload). - -##### `form.submitting` -This prop is passed for convenience (as you will need it to disable form inputs and button while a form is `Submitting`). Basically, this is `true` when `form.status` is `Submitting`, `false` otherwise. - -##### `form.result` -Use this function to get validation result for a field. - -```reason -switch (Email->form.result) { -| Some(Error(message)) => -
- message->React.string -
-| Some(Ok(Valid | NoValue)) -| None => React.null -} -``` - -##### `form.dirty` -This function will return `true` if any form field was touched, `false` otherwise. - -##### `form.valid` -This function will return `true` if all form field are valid, `false` otherwise. Not available in forms with async validations. - -##### `form.change` - -```reason -type change = (Form.field, Form.state) => unit; -``` - -This handler must be triggered `onChange`. It accepts `field` and the next form `state`. - -```reason -module MyForm = { - module EmailField = { - let update = (state, value) => {...state, email: value}; - }; -}; - - form.blur(Email)} - onChange={ - event => - form.change( - Email, - MyForm.EmailField.update( - form.state, - event->ReactEvent.Form.target##value, - ), - ) - } -/> -``` - -##### `form.blur` -This handler must be triggered `onBlur`. It accepts only one `field` argument. - -```reason - form.blur(Email)} -/> -``` - -##### `form.submit` -Use it as `onSubmit` handler of a `
` element: - -```reason -Formality.Dom.preventDefault} /> -``` - -##### `form.reset` -Resets form state. - -##### `form.dismissSubmissionResult` -Use it when you want to let user dismissing alerts with errors from server or success message without resetting a form. Under the hood, it changes `FormStatus.Submitted` & `FormStatus.SubmissionFailed` statuses back to `FormStatus.Editing`. - -##### `form.mapSubmissionError` -Maps over submission error. Useful for animating errors. - -##### `form.dismissSubmissionError` -Dismissing submission error only. - -### Async validations -Some validations can't be performed locally, e.g. on signup, you want to validate if user's email is available or it's already taken. - -There are 2 common ways to provide async feedback: send a request to a server on every change or only on blur event. The first way is better in terms of UX but creates a significant load, so your client might become slow or a server might feel bad. The blur way doesn't have this problem (at least not that much) but UX is definitely not the best b/c user have to blur away from a field to receive a feedback. - -What can we do about it to have the best of both worlds? An answer is to debounce async validations on change. What does it mean and how does it work: when a user types something in in a form field, no external requests are triggered. Instead, it's put on hold. While the user is typing, we're waiting. Once he stopped and there was no activity in the certain period of time—async request is triggered. - -#### Debounced async validations on change -To implement debounced async validations you need to make some additions to common form config. - -##### `let debounceInterval: int` -Configure amount of time (in ms) that `Formality` should wait since last user activity before invoking debounced async validation. - -```reason -/* Default interval: 700 */ -let debounceInterval = Formality.debounceInterval; - -/* Or any other custom int */ -let debounceInterval = 1000; -``` - -##### `validator` -In addition to `field`, `strategy`, `dependents` & `validate`, provide optional async validator. - -```reason -type asyncValidator('field, 'state, 'message) = { - field: 'field, - strategy: Formality.Strategy.t, - dependents: option(list('field)), - validate: 'state => Result.t(ok, 'message), - validateAsync: option( - ( - 'state => Js.Promise.t(Result.t(ok, 'message)), - ('state, 'state) => bool, - ), - ), -}; -``` - -`validateAsync` is a tuple that consists of 2 functions: - -```reason -( - /* 1. async validator */ - 'state => Js.Promise.t(Result.t(ok, 'message)), - - /* 2. value equality checker */ - ('state, 'state) => bool, -) -``` - -1. Validator itself takes `state` and returns `Js.Promise.t(Result.t)`. -2. Value equality checker receives 2 arguments: form state when validation was invoked and form state when response was resolved. Why it's required: by the time when server responded with some result, local value might be already changed so before setting received result `Formality` checks if value of the field is the same that was validated. And if it's not it simply ignores this result. - -**Please, make sure you check equality of field values, not state instances!** - -Example: - -```reason -validateAsync: Some( - ( - state => - Js.Promise.( - state.email - ->Api.validateEmail - ->then_( - valid => - valid ? - Ok(Valid)->resolve : - Error("Email is already taken")->resolve, - _, - ) - ), - (prevState, nextState) => prevState.email == nextState.email, - ) -), -``` - -To create form hook pass config to `Formality.Async.Make` functor: - -```reason -module MyAsyncFormHook = Formality.Async.Make(MyForm); -``` - -#### Async validations on blur -If you still want to use on blur validations just add `validateAsync` props to `validators` and use `Formality.Async.MakeOnBlur` to create form hook. - -#### Note on defining async validators -When you define async validator you need to local open `Async` module like this: - -```reason -module SignupForm = { - open Formality; - - ... - - module EmailField = { - let validator = Async.{ field: Email, ... }; - }; -}; -``` - -You will probably get `Warning 45` from compiler. You can address it either by: -1. Disabling this warning in `bsconfig.json`. - -```json -"warnings": { - "number": "-45" -} -``` - -2. Or change local open to this: - -```reason -let validator = { - Async.field: Email, - strategy: OnFirstSuccessOrFirstBlur, - ... -}; -``` -### Custom field comparator - -By default Formality uses `Pervasives.compare` to compare fields internally. This works when forms are simple, but can cause troubles when fields contain incomparable values, like functions. In such case `Pervasives.compare` will throw exception. -You can create form with your own comparator -```reason -module LoginForm = { - type field = - | Email - | Password - | RememberMe; - - type state = { - email: string, - password: string, - rememberMe: bool, - }; - - type message = string; - type submissionError = unit; - - let validators = []; - - module FieldId = - Belt.Id.MakeComparable({ - type t = field; - let cmp = Pervasives.compare; - }); -}; -module LoginForm = Formality.MakeWithId(LoginForm); -``` -Each Formality form functor has such helper-functor that requires FieldId - -```reason -Formality.MakeWithId -Formality.Async.MakeWithId -Formality.Async.MakeOnBlurWithId -``` - -[Custom comparator usage examples](/examples/FormsWithFieldId.re) - -### I18n -If you build i18n'ized app then set `message` type in form config to your `I18n.t` type. E.g.: - -```reason -type message = I18n.t; -``` - ## Alternatives - - [ReForm](https://github.com/Astrocoders/reform)
Alternative form state management solution for ReasonReact apps. diff --git a/docs/01-Installation.md b/docs/01-Installation.md new file mode 100644 index 00000000..8d4dfc35 --- /dev/null +++ b/docs/01-Installation.md @@ -0,0 +1,26 @@ +# Installation +To get started, add `re-formality` to your project either via `npm` or `yarn`: + +```shell +# yarn +yarn add re-formality@next +# or npm +npm install --save re-formality@next +``` + +Under the hood, `re-formality` implemented as PPX. So you need to add it to both—`bs-dependencies` and `ppx-flags`—arrays in your `bsconfig.json`: + +```json +"bs-dependencies": [ + "re-formality" +], +"ppx-flags": [ + "re-formality/ppx" +], +``` + +Before proceeding with actual code, we will elaborate on some core concepts that this library implements. + +--- + +Next: **[Validation Strategies →](./02-ValidationStrategies.md)** diff --git a/docs/02-ValidationStrategies.md b/docs/02-ValidationStrategies.md new file mode 100644 index 00000000..20c8dbae --- /dev/null +++ b/docs/02-ValidationStrategies.md @@ -0,0 +1,45 @@ +# Validation Strategies +The main purpose of this library is to provide great form validation UX. To achieve this, `Formality` follows the following principle: + +

+Validation feedback should be provided as soon as possible but not too soon. +

+ +The hardest part is to figure out the right moment when first validation results should be emitted in UI. + +Let's break down a case with credit card field. A user opens a form and focuses on a field. When the first character is typed, the field is in an invalid state but it's not polite to show an error immediately: we should let user a chance to finish what he's doing. While the user is typing and field is in invalid state, we don't provide any feedback in UI. If after some character validator reported valid result, it's a proper moment to indicate a success (e.g. show credit card type). But if the user left the field in an invalid state (e.g. moved to another field) we have all rights to emit an error. After the first result is emitted, we update validation state in UI on every change. + +Sadly, form fields are different and credit card scenario is not universal. This is where strategies kick in. + +### Strategies +We can't have a single scenario for all the cases but we can spot the most common ones, describe a logic of each and apply proper scenarios to specific form fields. To understand a behavior of each strategy, add the following prefix to its name: _"Start providing feedback in UI on..."_ + +```reason +module Strategy = { + type t = + | OnFirstBlur + | OnFirstChange + | OnFirstSuccess + | OnFirstSuccessOrFirstBlur + | OnSubmit; +}; +``` + +#### `OnFirstBlur` +Results are emitted on the first blur. After first results are emitted, a user receives feedback on every change in this field. + +#### `OnFirstChange` +Results are emitted on the first change in a field (basically, as a user types). + +#### `OnFirstSuccess` +Results are emitted on first successful validation. After first results are emitted, a user receives feedback on every change in this field. + +#### `OnFirstSuccessOrFirstBlur` ✨ +Results are emitted on first successful validation or on the first blur. After first results are emitted, a user receives feedback on every change in this field. + +#### `OnSubmit` +Results are emitted only after the first submission attempt. After this, results for each field are emitted on every change until the form is reset. + +--- + +Next: **[IO →](./03-IO.md)** diff --git a/docs/03-IO.md b/docs/03-IO.md new file mode 100644 index 00000000..e445fc96 --- /dev/null +++ b/docs/03-IO.md @@ -0,0 +1,17 @@ +# IO +We care about safety and correctness of forms as much as we care about UX aspect. So one of the goals of this library is to ensure that input data from UI can be safely turned into expected and correct result. + +The core types everything is based on are `input` and `output`. `input` is representation of a raw form state in UI. `output` is a representation of valid form data. The whole process of filling out the form is considered as a process of deserialization of raw data (strings / booleans from DOM or any other environment) into type safe data structure via `validators`—user defined functions that ensure validity of an `input`. We guarantee on a type level that if form gets submitted, the types of submittable data are correct. + +To illustrate the IO concept, consider an `email` field in a form. In UI, email represented as a string. But in an application domain it should be of `Email.t` type which holds parsed representation of valid email. So the expected result of an email field should be of `Email.t` type. + +```reason +type input = {email: string}; +type output = {email: Email.t}; +``` + +To turn the former into the latter, you must define function, which takes `input` and returns `result`: either `Ok(Email.t)` or `Error(message)`. More on this, in the next sections. + +--- + +Next: **[Basic Usage →](./04-BasicUsage.md)** diff --git a/docs/04-BasicUsage.md b/docs/04-BasicUsage.md new file mode 100644 index 00000000..3d01c73b --- /dev/null +++ b/docs/04-BasicUsage.md @@ -0,0 +1,388 @@ +# Basic Usage +It takes 2 steps to implement a form: + +1. Create a form hook. +2. Render a form UI. + +## Creating a form hook +Form hook can be created using `[%form]` ppx extension. It requires at least 2 things: + +- `input` type which must be a record +- `validators` record + +Let's start with the `input`: + +```reason +module LoginForm = [%form + type input = { + email: string, + password: string, + }; +]; +``` + +As mentioned in [**IO**](./03-IO.md) section, there should be an `output` type defined somewhere. If it's not provided, then under the hood it gets aliased to an `input` type. So the generated code would look like this: + +```reason +module LoginForm = [%form + type input = { + email: string, + password: string, + }; + type output = input; +]; +``` + +But since we want to deserialize form input into type-safe representation, we will provide our own `output` type with the `email` field set to `Email.t` type. + +```reason +module LoginForm = [%form + type input = { + email: string, + password: string, + }; + type output = { + email: Email.t, + password: string, + }; +]; +``` + +Worth mentioning, fields in the `output` type must be the same as in `input` type. Otherwise, it would be a compile-time error. + +Another important detail regarding the `output` type is that you can't use external types as a value of this type. It must be a record defined in this module. I.e. this wouldn't work: + +```reason +type output = LoginData.t; +``` + +One more optional type that is involved here is `message`—the type of error messages that would be displayed in UI. If an app doesn't implement internalization, you can skip this type and it would be set to `string` (this is what we're going to do in the current example). Otherwise, feel free to use your own type here. See **[I18n](./10-I18n.md)** section for more details. + +The next thing to implement is a `validators`: a record with the same set of fields as in `input`/`output`, each holds instructions on how to validate a field. Let's implement one for `email` field, assuming that somewhere in the app there is an `Email` module that defines `Email.t` type and `Email.validate` function which takes `string` and returns `result(Email.t, string)`. + +```reason +// Email.validate: string => result(Email.t, string) +let validators = { + email: { + strategy: OnFirstSuccessOrFirstBlur, + validate: input => input.email->Email.validate, + }, +}; +``` + +First of all, you don't need to define a type for `validators`. It's already done by the ppx. In the simplest possible case, field validator record has 2 entries: +1. `strategy`: as described in **[Validation Strategies](./02-ValidationStrategies.md)** section +2. `validate`: function that takes `input` as argument and returns `result([OUTPUT_TYPE_OF_FIELD], message)`. In the `email` case, it's `result(Email.t, message)`. + +If field shouldn't be validated, set its validator to `None`: + +```reason +let validators = { + field: None, +}; +``` + +Pretty much the same applies to the `password` field: + +```reason +let validators = { + password: { + strategy: OnFirstBlur, + validate: input => + switch (input.password) { + | "" => Error("Password is required") + | _ => Ok(input.password) + }, + }, +}; +``` + +Looks like we're done with the first step: + +```reason +module LoginForm = [%form + type input = { + email: string, + password: string, + }; + + type output = { + email: Email.t, + password: string, + }; + + let validators = { + email: { + strategy: OnFirstSuccessOrFirstBlur, + validate: input => input.email->Email.validate, + }, + password: { + strategy: OnFirstBlur, + validate: input => + switch (input.password) { + | "" => Error("Password is required") + | _ => Ok(input.password) + }, + }, + }; +]; +``` + +## Rendering UI +The resulting module exposes the `useForm` hook that we are going to use for rendering form UI. + +### `useForm` hook +```reason +[@react.component] +let make = () => { + let form = + LoginForm.useForm( + ~initialInput={email: "", password: ""}, + ~onSubmit=(output, cb) => { + // Skipping this for now... + }, + ); +}; +``` + +`useForm` hook takes 2 arguments: +1. `initialInput`: a record of `input` type with initial field values +2. `onSubmit` function that takes `output` record and one more argument with a set of callbacks. We will get back to this a bit later. + +As a result, we get a `form` record that holds everything we need to render UI. + +### `` tag +Let's start with the `` tag: + +```reason +[@react.component] +let make = () => { + let form = LoginForm.useForm(...); + + form.submit()}> + ... +
+}; +``` + +To trigger submission, you need to call `form.submit` function. The best place to do this is `onSubmit` prop of a `
` tag. For clarity's sake, we skipped some DOM-related steps, but you can inspect [`Form`](../examples/src/Form.re) component from examples which includes those. + +### Text input field +Next thing to render is a text input for `email` field: + +```reason + form.blurEmail()} + onChange={event => { + let value = event->ReactEvent.Form.target##value; + form.updateEmail(input => {...input, email: value}); + }} +/> +``` + +The value of the field is exposed via `form.input` record. For extra safety, we disable all inputs during form submission using `form.submitting` property which is of boolean type. The next 2 functions are very important: +1. `form.blurEmail: unit => unit`: must be triggered from `onBlur` handler of an input field +2. `form.updateEmail: (input => input) => unit`: must be triggered from `onChange` handler of an input field. It takes a function as an argument which takes the current form `input` and must return updated `input` record. + +Please, make sure you don't capture the whole `event` in this callback. Otherwise, it would result in a runtime error. + +```reason +// Bad, don't do this! +onChange={event => { + form.updateEmail(input => { + ...input, + email: event->ReactEvent.Form.target##value, + }); +}} + +// Good: extract value from event before passing it to the callback +onChange={event => { + let value = event->ReactEvent.Form.target##value; + form.updateEmail(input => {...input, email: value}); +}} +``` + +This runtime error happens due to [React's `SyntheticEvent` being pooled](https://reactjs.org/docs/events.html#event-pooling). Since callback gets triggered asynchronously, by the time it gets called, the event is already null'ed by React. + +### Messages +To display feedback in UI, we can use `form.emailResult` value. It's exactly what email validator returns but wrapped in `option` type: + +```reason +{switch (form.emailResult) { + | Some(Error(message)) => +
message->React.string
+ | Some(Ok(_)) + | None => React.null + }} +``` + +When its value is `None`, it means it's not yet a good time to display any feedback to a user, according to the chosen strategy. + +The same steps should be done for the `password` field. + +### Submit button +Nothing special here: + +```reason + +``` + +### Form submission +One more thing that needs to be handled is the submission of the form. When a user hits submit and the data is valid, hook triggers `onSubmit` function that was passed to it. + +The implementation of this handler is always app-specific. When `onSubmit` handler gets triggered it receives 2 arguments: `output` of the form and set of callbacks that you might want to trigger in specific circumstances or just ignore them and do your own thing according to the requirements of your app. + +In general, you would want to take the output and send it to your server asynchronously. When a response is received, there might be many scenarios: +- on success, redirect a user to another screen +- on success, reset the form +- on error, show errors from the server, etc. + +In this example, we would stick with the simplest one. And elaborate on more advanced scenarios in **[Form Submission](./09-FormSubmission.md)** section. + +So, the scenario is: +- on success, store a user in the app state and redirect the user to another screen +- on failure, display a generic error message + +Assuming, there is `Api.loginUser` function in the app: + +```reason +let form = + LoginForm.useForm( + ~initialInput={email: "", password: ""}, + ~onSubmit=(output, cb) => { + output->Api.loginUser(res => switch (res) { + | Ok(user) => user->AppShell.loginAndRedirect + | Error() => cb.notifyOnFailure() + }); + }, + ); +``` + +When submission succeeded, the user gets redirected to another screen and form gets unmounted. At this point, we don't really care about its state and just fire `AppShell.loginAndRedirect` handler provided by the app (it's not specific to `Formality`). + +But when submission fails, we need to display an error message in UI. So we need to let form hook know about failed submission by triggering `cb.notifyOnFailure()` handler passed in the second argument. What happens next? + +Here, we need to mention `form.status`. Form hook tracks the status of the whole form which can be in the following states: + +```reason +type formStatus('submissionError) = + | Editing + | Submitting(option('submissionError)) + | Submitted + | SubmissionFailed('submissionError); +``` + +When `notifyOnFailure()` is triggered, form gets switched to the `SubmissionFailed()` status. So you can react on this change in the UI: + +```reason +switch (form.status) { +| Editing +| Submitting(_) +| Submitted => React.null +| SubmissionFailed() => +
+ "Not logged in"->React.string +
+} +``` + +### Wrapping up +The whole implementation: + +```reason +module LoginForm = [%form + type input = { + email: string, + password: string, + }; + + type output = { + email: Email.t, + password: string, + }; + + let validators = { + email: { + strategy: OnFirstSuccessOrFirstBlur, + validate: input => input.email->Email.validate, + }, + password: { + strategy: OnFirstBlur, + validate: input => + switch (input.password) { + | "" => Error("Password is required") + | _ => Ok(input.password) + }, + }, + }; +]; + +[@react.component] +let make = () => { + let form = + LoginForm.useForm( + ~initialInput={email: "", password: ""}, + ~onSubmit=(output, cb) => { + output->Api.loginUser(res => switch (res) { + | Ok(user) => user->AppShell.loginAndRedirect + | Error() => cb.notifyOnFailure() + }); + }, + ); + + form.submit()}> + form.blurEmail()} + onChange={event => { + let value = event->ReactEvent.Form.target##value; + form.updateEmail(input => {...input, email: value}); + }} + /> + {switch (form.emailResult) { + | Some(Error(message)) => +
message->React.string
+ | Some(Ok(_)) + | None => React.null + }} + form.blurPassword()} + onChange={event => { + let value = event->ReactEvent.Form.target##value; + form.updatePassword(input => {...input, password: value}); + }} + /> + {switch (form.passwordResult) { + | Some(Error(message)) => +
message->React.string
+ | Some(Ok(_)) + | None => React.null + }} + + {switch (form.status) { + | Editing + | Submitting(_) + | Submitted => React.null + | SubmissionFailed() => +
+ "Not logged in"->React.string +
+ }} +
+}; +``` + +This is the most basic example which shows only a subset of use-cases. To find out more about advanced features, proceed to the next sections. + +--- + +Next: **[Async Validation →](./05-AsyncValidation.md)** diff --git a/docs/05-AsyncValidation.md b/docs/05-AsyncValidation.md new file mode 100644 index 00000000..966d4492 --- /dev/null +++ b/docs/05-AsyncValidation.md @@ -0,0 +1,130 @@ +# Async Validation +Some validations can't be performed locally. E.g. on signup, you want to validate if a user's email is available or it's already taken. + +There are 2 common ways to provide async feedback: send a request to a server on every change or only on blur event. The first way is better in terms of UX but creates a significant load, so your client might become slow or a server might feel bad. The blur way doesn't have this problem (at least not that much) but UX is definitely not the best because a user has to left a field to receive feedback. + +What can we do about it to have the best of both worlds? An answer is to debounce async validations on change. What does it mean and how does it work: when a user types something in in a form field, no external requests are triggered. Instead, it's put on hold. While the user is typing, we're waiting. When the user stopped and there was no activity in a certain period of time—async request gets triggered. + +## Debounced async validations on change +To implement debounced async validations, you need to annotate your input field: + +```reason +type input = { + email: [@field.async] string, +}; +``` + +And update the validator: in addition to the `strategy` and `validate` entries, add `validateAsync` function which takes value of `output` type of the field and returns `Js.Promise.t(result([OUTPUT_TYPE_OF_FIELD], message))`. In case of the `email` field, it would be `Js.Promise.t(result(Email.t, message))`. + +```reason +type input = { + email: [@field.async] string, +}; + +type output = { + email: Email.t, +}; + +let validators = { + email: { + strategy: OnFirstSuccessOrFirstBlur, + validate: input => input.email->Email.validate, + validateAsync: email => + Js.Promise.( + Api.validateEmail(email) + ->then_( + valid => + valid + ? Ok(email)->resolve + : Error("Email is already taken")->resolve, + _, + ) + ), + }, +}; +``` + +On the rendering side of things, there is only one change. The type of field result is a bit different: + +```reason +type asyncFieldStatus('outputValue, 'message) = + | Validating('outputValue) + | Result(result('outputValue, 'message)); +``` + +So in UI it would look like this: + +```reason +{switch (form.emailResult) { + | Some(Validating(_)) => + | Some(Result(Error(message))) => +
message->React.string
+ | Some(Result(Ok(_))) + | None => React.null + }} +``` + +### Additional configuration options +#### Debounce interval +You can configure the amount of time (in ms) that `Formality` should wait since last user activity before invoking debounced async validation. By default, it's set to `700` bu you can change this by providing your own value in the config like this: + +```reason +type input = ...; +type output = ...; + +let debounceInterval = 1000; + +let validators = ...; +``` + +#### Value equality +One more thing that you might want to configure is the value equality function. + +It takes some time to get a response from serve after async validation is triggered. By the time when server responded with some result, the local value might be already changed so before setting received result `Formality` checks if the value of the field is the same that was validated. And if it's not it ignores this result. To perform such check, it uses `validator.eq` function which is by default set to `(==)`. + +When you would want to change it? Consider the `Email.t` type being a record under the hood: + +```reason +module Email = { + type t = { + user: string, + domain: string, + }; +}; +``` + +Efficiently, 2 emails are equal when their `user` and `domain` fields are equal. So such type can implement own equality function which would be more efficient performance-wise than the standard `(==)`: + +```reason +module Email = { + type t = { + user: string, + domain: string, + }; + + let (==) = (x1, x2) => x1.user == x2.user && x1.domain == x2.domain; +}; + +// And then provide it to validators +let validators = { + email: { + strategy: OnFirstSuccessOrFirstBlur, + validate: input => ..., + validateAsync: email => ..., + eq: Email.(==), + }, +}; +``` + +## Async validations on blur +If you want to trigger async validations on blur, define mode explicitly: + +```reason +type input = { + email: [@field.async {mode: OnBlur}] string, +}; +``` + +--- + +Next: **[Collections →](./06-Collections.md)** diff --git a/docs/06-Collections.md b/docs/06-Collections.md new file mode 100644 index 00000000..5670135f --- /dev/null +++ b/docs/06-Collections.md @@ -0,0 +1,161 @@ +# Collections +Collection is an array that contains sets of fields that can be added or removed from a form. + +## Configuration +To define a collection, you need to annotate a field in the `input` record with `[@field.collection]` attribute: + +```reason +type input = { + authors: [@field.collection] array(author), +} +and author = {name: string}; +``` + +Such field has few requirements: +1. It must be of `array(entry)` type +2. `entry` type must be a record which definition is inlined in the configuration module +3. `[@field.collection]` can't be combined with other attributes but each field in `entry` record can be handled as any other field in the `input` record. E.g. you can do this: + +```reason +type input = { + authors: [@field.collection] array(author), +} +and author = {name: [@field.async] string}; +``` + +Also, make sure the naming is consistent. E.g. annotated `authors` field (plural) holds an array of `author` records (singular). + +When the `output` type is different, the implementation might look like this: + +```reason +type input = { + authors: [@field.collection] array(author), +} +and author = {name: string}; + +type output = { + authors: array(author'), +} +and author' = {name: Author.name}; +``` + +Collection validator has the following shape: + +```reason +authors: { + collection: input => result(unit, message), + fields: { + name: (input, ~at: int) => result([OUTPUT_TYPE_OF_FIELD], message), + } +} +``` + +Function under `collection` key validates collection as a whole. E.g. you might want to ensure that there is at least 1 author exists. If you don't want to perform any checks, set `collection: None`. + +`fields` record holds fields of collection entry. It works the same way as with the general `input` field with small addition: validator function receives `~at` named argument which defines an index of entity that's being validated. + +Here is an example of implementation: + +```reason +let validators = { + authors: { + collection: input => + switch (input.authors) { + | [||] => Error("There must be at least one author") + | _ => Ok() + }, + fields: { + name: { + strategy: OnFirstSuccessOrFirstBlur, + validate: (input, ~at) => { + switch (input.authors->Array.getUnsafe(at)) { + | {name: ""} => Error("Author name is required") + | {name} => Ok(name) + }; + }, + }, + }, + }, +}; +``` + +Note that collections are not recursive, i.e. you can't have nested collections, one in another. + +## Rendering +Getting the input and results, as well as handling addition, removal and field updates of the collection are possible via functions provided by the `useForm` hook. In the case of `authors` collection, you can use the following: + +- `form.addAuthor({name: ""})`: adds `author` entry to collection +- `form.removeAuthor(~at: index)`: removes `author` entry from collection +- `form.blurAuthorName(~at: index)`: triggers blur in `author.name` field at index +- `form.updateAuthorName(~at: index, input => input)`: updates `author.name` field at index +- `form.authorNameResult(~at=index)`: returns validation result for `author.name` field at index +- `form.authorsResult`: returns result of the whole collection validation, if validator exists + +And this is how it might look like in UI: + +```reason +let form = MyForm.useForm(...); + +
+ { + form.input.authors + ->Array.mapWithIndex((index, author) => + <> + form.blurAuthorName(~at=index)} + onChange={ + event => { + let value = event->ReactEvent.Form.target##value; + form.updateAuthorName(~at=index, input => + { + ...input, + authors: + form.input.authors + ->Array.mapWithIndex((idx, author) => + if (idx != index) { + author; + } else { + {name: value}; + } + ), + } + ); + } + } + /> + + { + switch (form.authorNameResult(~at=index)) { + | Some(Error(message)) => +
message->React.string
+ | Some(Ok(_)) + | None => React.null + } + } + + ) + ->React.array + } + + {switch (form.authorsResult) { + | Some(Error(message)) => +
+ message->React.string +
+ | Some(Ok ()) + | None => React.null + }} +
+``` + +--- + +Next: **[Dependent Fields →](./07-DependentFields.md)** diff --git a/docs/07-DependentFields.md b/docs/07-DependentFields.md new file mode 100644 index 00000000..576a5666 --- /dev/null +++ b/docs/07-DependentFields.md @@ -0,0 +1,47 @@ +# Dependent Fields +If the validity of one field depends on the value of another, use `[@field.deps]` attribute to mark the relation. + +```reason +type input = { + a: [@field.deps b] string, + b: string, +}; +``` + + In the case above, it states: "If the value of field `a` has changed, please, re-validate field `b` as well". + +A real-world use-case is a form that updates a password: + +```reason +type input = { + oldPassword string, + newPassword: [@field.deps newPasswordConfirmation] string, + newPasswordConfirmation: string, +}; +``` + +When `newPassword` field is updated, `newPasswordConfirmation` field should be revalidated as well since its validity depends on the `newPassword` value. + +If you need to re-validate multiple fields, provide a tuple: + +```reason +type input = { + a: [@field.deps (b, c)] string, + b: string, + c: int, +}; +``` + +If one of the dependent fields is a field of collection, define it like this: + +```reason +type input = { + title: [@field.deps author.name] string, + authors: [@field.collection] array(author), +} +and author = {name: string} +``` + +--- + +Next: **[Form Submission →](./08-FormSubmission.md)** diff --git a/docs/08-FormSubmission.md b/docs/08-FormSubmission.md new file mode 100644 index 00000000..f978a2ff --- /dev/null +++ b/docs/08-FormSubmission.md @@ -0,0 +1,62 @@ +# Form Submission +`Formality` keeps track of the whole form status which can be in the following states: + +```reason +type formStatus('submissionError) = + | Editing + | Submitting(option('submissionError)) + | Submitted + | SubmissionFailed('submissionError); +``` + +As it's been shown in **[Basic Usage](./04-BasicUsage.md)** section, to trigger form submission you need to call `form.submit` function. After that, `Formality` validates the whole form and if it's valid, it triggers `onSubmit` handler that's been passed to `useForm` hook. Before `onSubmit` gets triggered, `Formality` switches its status to `Submitting` and passes control to the application. Since it's not aware when the application is done with the submission, the latter should notify abstraction about the result so it would switch form status to either `Editing`, `Submitted` or `SubmissionFailed`. + +## Submission callbacks +`onSubmit` handler takes 2 arguments: `output` data and callbacks. Let's look into the latter. + +```reason +type submissionCallbacks('input, 'submissionError) = { + notifyOnSuccess: option('input) => unit, + notifyOnFailure: 'submissionError => unit, + reset: unit => unit, + dismissSubmissionResult: unit => unit, +}; +``` + +### `notifyOnSuccess: option('input) => unit` +This callback should be triggered after successful submission if you don't want to completely reset the form but set it to `Submitted` state preserving all internal statuses. Optionally, you can pass the next `input` that would replace the current one. + +### `notifyOnFailure: 'submissionError => unit` +If submission fails, use this callback to notify hook about it. It takes one argument: `submissionError`. + +When a form gets submitted, submission might fail for various reasons. It might be a bad password on a login attempt (expected error) or server crash (unexpected error), anything. This kind of error is specific to a form and you can provide an exact reason why submission failed via `notifyOnFailure` callback. + +To be able to do this, you need to declare `submissionError` type in the config. E.g.: + +```reason +type input = ...; +type output = ...; + +type submissionError = + | UserNotFound + | BadPassword + | UnexpectedServerError; +``` + +Then, when a response from the server is received, you can pass one of those constructors to the `notifyOnFailure` and it will be available via `form.status` variant in `SubmissionFailed(submissionError)` constructor. + +If you don't need to parse submission errors in your form, skip this type and it would be set to `unit` under the hood: + +```reason +type submissionError = unit; +``` + +### `reset: unit => unit` +This callback simply resets the form: its status gets set back to `Editing`, fields statuses get reset as well. + +### `dismissSubmissionResult: unit => unit` +Use it when you want to dismiss alerts with errors from a server or success message without resetting a form. Under the hood, it changes a form status from `Submitted` or `SubmissionFailed` to `Editing`. + +--- + +Next: **[I18n →](./09-I18n.md)** diff --git a/docs/09-I18n.md b/docs/09-I18n.md new file mode 100644 index 00000000..7f2bbe51 --- /dev/null +++ b/docs/09-I18n.md @@ -0,0 +1,12 @@ +# I18n +By default, all error messages are `strings`. But if you build an i18n'ized app, you can set `message` type in the form config to own type. E.g.: + +```reason +type message = I18n.t; +``` + +Then errors returned from all validators must be of this type. + +--- + +Next: **[Caveats →](./10-Caveats.md)** diff --git a/docs/10-Caveats.md b/docs/10-Caveats.md new file mode 100644 index 00000000..eeb493dc --- /dev/null +++ b/docs/10-Caveats.md @@ -0,0 +1,17 @@ +# Caveats +## Warning 42 +This warning is disabled by default but if you explicitly enabled it, you might see the following message during a compilation: + +``` +Warning 42: this use of [field] relies on type-directed disambiguation, it will not compile with OCaml 4.00 or earlier. +``` + +To get rid of it, consider to disable it either globally via `bsconfig.json` or locally in form modules: + +```reason +[@ocaml.warning "-42"]; +``` + +--- + +Next: **[API →](./11-API.md)** diff --git a/docs/11-API.md b/docs/11-API.md new file mode 100644 index 00000000..4af2c120 --- /dev/null +++ b/docs/11-API.md @@ -0,0 +1,438 @@ +# API + +- [Configuration](#configuration) + - [`type input`](#type-input) + - [`[@field.async]`](#fieldasync) + - [`[@field.collection]`](#fieldcollection) + - [`[@field.deps]`](#fielddeps) + - [`type output`](#type-output) + - [`type message`](#type-message) + - [`type submissionError`](#type-submissionerror) + - [`let debounceInterval`](#let-debounceinterval) + - [`let validators`](#let-validators) +- [Rendering](#rendering) + - [`useForm`](#useform) + - [`submissionCallbacks`](#submissioncallbacks) + - [`interface`](#interface) + - [Update handlers](#update-handlers) + - [Blur handlers](#blur-handlers) + - [Field result](#field-result) + - [Collection handlers](#collection-handlers) + - [`input`](#input) + - [`status`](#status) + - [`submitting`](#submitting) + - [`dirty`](#dirty) + - [`submit`](#submit) + - [`dismissSubmissionResult`](#dismisssubmissionresult) + - [`dismissSubmissionError`](#dismisssubmissionerror) + - [`mapSubmissionError`](#mapsubmissionerror) + - [`reset`](#reset) + - [`valid`](#valid) + +## Configuration +Form configuration module can be created using `[%form]` extension: + +```reason +module MyForm = [%form + type input; + type output; + ... +]; +``` + +### `type input` +**Requirements:** Required. Must be a record. + +Form input data. + +```reason +type input = {email: string}; +``` + +See **[IO](./03-IO.md)** & **[Basic Usage](./04-BasicUsage.md)**. +
+
+ +#### `input` field attributes +##### `[@field.async]` +**Requirements:** Optional. + +Attribute for a field with async validation. + +```reason +type input = {email: [@field.async] string}; + +// OnChange mode +// Default, use it if you want to be explicit. +type input = {email: [@field.async {mode: OnChange}] string}; + +// OnBlur mode +type input = {email: [@field.async {mode: OnBlur}] string}; +``` + +See **[Async Validation](./05-AsyncValidation.md)**. +
+
+ +##### `[@field.collection]` +**Requirements:** Optional. If provided, must be an array. + +Attribute for a collection field. + +```reason +type input = { + authors: [@field.collection] array(author), +} +and author = {name: string}; +``` + +See **[Collections](./06-Collections.md)**. +
+
+ +##### `[@field.deps]` +**Requirements:** Optional. + +Dependent fields attribute. + +```reason +type input = { + a: [@field.deps b] string, + b: string, +}; +``` + +See **[Dependent Fields](./07-DependentFields.md)**. +
+
+ +### `type output` +**Requirements:** Optional. If provided, must be a record with the same set of fields as in `input`.
+**Default:** `input` + +Form output data. + +```reason +type output = {email: Email.t}; +``` + +See **[IO](./03-IO.md)** & **[Basic Usage](./04-BasicUsage.md)**. +
+
+ +### `type message` +**Requirements:** Optional.
+**Default:** `string` + +Type of error messages. + +```reason +type message = I18n.t; +``` + +See **[Basic Usage](./04-BasicUsage.md)** & **[I18n](./09-I18n.md)**. +
+
+ +### `type submissionError` +**Requirements:** Optional.
+**Default:** `unit` + +Type of submission error. + +```reason +type submissionError = + | UserNotFound + | UnknownError; +``` + +See **[Form Submission](./08-FormSubmission.md)**. +
+
+ +### `let debounceInterval` +**Requirements:** Optional `int`.
+**Default:** `700` + +Debounce interval in ms for async validators in `OnChange` mode. + +```reason +let debounceInterval = 1000; +``` + +See **[Async Validation](./05-AsyncValidation.md)**. +
+
+ +### `let validators` +**Requirements:** Required. Must be a record with the same set of fields as in `input`/`output`.
+ +Validators record. + +```reason +// Pseudo code +let validators = { + // General field + field: { + strategy: Strategy.t, + validate: input => result('outputValue, message), + }, + + // Field of collection + fieldOfCollection: { + collection: input => result(unit, message), // or None + fields: { + strategy: Strategy.t, + validate: (input, ~at: int) => result('outputValue, message), + }, + }, + + // Async field + asyncField: { + strategy: Strategy.t, + validate: input => result('outputValue, message), + validateAsync: 'outputValue => Js.Promise.t(result('outputValue, message)), + }, + + // Async field of collection + asyncFieldOfCollection: { + strategy: Strategy.t, + validate: (input, ~at: int) => result('outputValue, message), + validateAsync: 'outputValue => Js.Promise.t(result('outputValue, message)), + }, + + // Async field with eq function + asyncFieldWithEq: { + strategy: Strategy.t, + validate: input => result('outputValue, message), + validateAsync: 'outputValue => Js.Promise.t(result('outputValue, message)), + eq: ('outputValue, 'outputValue) => bool, + }, + + // Field without validator + fieldWithoutValidator: None, +}; +``` + +See **[Validation Strategies](./02-ValidationStrategies.md)**, **[Basic Usage](./04-BasicUsage.md)**, **[Async Validation](./05-AsyncValidation.md)** & **[Collections](./06-Collections.md)**. +
+
+ +## Rendering +Module created via `[%form]` extension exposes `useForm` React hook. + +### `useForm` +React hook. + +```reason +MyForm.useForm( + ~initialInput: MyForm.input, + ~onSubmit: (output: MyForm.output, cb: Formality.submissionCallbacks) => unit, +) => MyForm.interface; +``` + +#### `submissionCallbacks` +Callbacks passed to `onSubmit` handler. + +```reason +type submissionCallbacks = { + notifyOnSuccess: option(input) => unit, + notifyOnFailure: submissionError => unit, + reset: unit => unit, + dismissSubmissionResult: unit => unit, +}; +``` + +See **[Form Submission](./08-FormSubmission.md)**. +
+
+ +### `interface` +Interface to the hook. + +```reason +type interface = { + input: input, + status: Formality.formStatus(submissionError), + submitting: bool, + dirty: unit => bool, + submit: unit => unit, + dismissSubmissionResult: unit => unit, + dismissSubmissionError: unit => unit, + mapSubmissionError: (submissionError => submissionError) => unit, + reset: unit => unit, + + // General form + valid: unit => bool, + // Form with async fields + valid: unit => option(bool), + + // General field + update[Field]: (input => input) => unit, + blur[Field]: unit => unit, + [field]Result: option(result('outputValue, message)), + + // Async field + update[Field]: (input => input) => unit, + blur[Field]: unit => unit, + [field]Result: option(Formality.Async.exposedFieldStatus('outputValue, message)), + + // Field of collection + update[CollectionEntry][Field]: (input => input, ~at: index) => unit, + blur[CollectionEntry][Field]: (~at: index) => unit, + [collectionEntry][Field]Result: (~at: index) => option(result('outputValue, message)), + + // Collection + add[CollectionEntry]: 'collectionEntryInput => unit, + remove[CollectionEntry]: (~at: index) => unit, +}; +``` +
+ +#### Update handlers +Used to update form input for a specific field: + +```reason +// Field +update[Field]: (input => input) => unit + +// Field of collection +update[CollectionEntry][Field]: (input => input, ~at: index) => unit, +``` +
+ +#### Blur handlers +Used to notify hook on blur event for a specific field: + +```reason +// Field +blur[Field]: unit => unit + +// Field of collection +blur[CollectionEntry][Field]: (~at: index) => unit, +``` +
+ +#### Field result +Used to display validation result for a specific field: + +```reason +// Field +[field]Result: option(result('outputValue, message)), + +// Async field +type asyncResult = + | Validating('outputValue) + | Result(result('outputValue, message)); + +[field]Result: option(asyncResult), + +// Field of collection +[collectionEntry][Field]Result: (~at: index) => option(result('outputValue, message)), +``` +
+ +#### Collection handlers +Used to update collection contents: + +```reason +// Add entry +add[CollectionEntry]: 'collectionEntryInput => unit, + +// Remove entry +remove[CollectionEntry]: (~at: index) => unit, +``` +
+ +#### `input` +Current form input. Use it to set values of form fields. + +```reason +input: MyForm.input +``` +
+ +#### `status` +Current form status. + +```reason +type formStatus = + | Editing + | Submitting(option(MyForm.submissionError)) + | Submitted + | SubmissionFailed(MyForm.submissionError); + +status: formStatus +``` +
+ +#### `submitting` +`bool` value, passed for convenience as it gets derived from the `form.status`. Set to `true` when `form.status` is `Submitting`, `false` otherwise. + +```reason +submitting: bool +``` +
+ +#### `dirty` +The function would return `true` if any form field was touched, `false` otherwise. + +```reason +dirty: unit => bool +``` +
+ +#### `submit` +Triggers form submission. + +```reason +submit: unit => unit +``` +
+ +#### `dismissSubmissionResult` +Use it when you want to let a user dismiss alerts with errors from a server or success message without resetting a form. Under the hood, it changes `Submitted` or `SubmissionFailed` form statuses back to `Editing`. + +```reason +dismissSubmissionResult: unit => unit +``` +
+ +#### `dismissSubmissionError` +Dismisses submission error only. + +```reason +dismissSubmissionError: unit => unit +``` +
+ +#### `mapSubmissionError` +Maps over submission error. Useful for animating errors. + +```reason +mapSubmissionError: (submissionError => submissionError) => unit +``` +
+ +#### `reset` +Resets the whole form to its initial state. + +```reason +reset: unit => unit +``` +
+ +#### `valid` +The function that would report the overall validity of the form. + +In forms with async fields, it would return `option(bool)`. `None` is possible when one of the async fields in `Validating` state, i.e. we don't know yet if it's valid or not. Also, keep in mind, if one of the async fields wasn't touched yet, it would perform only local validation. + +Use this function with caution since it might introduce noticeable overhead on large forms. + +```reason +// General form +valid: unit => bool + +// Form with async fields +valid: unit => option(bool) +``` diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..b0b2cc12 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,13 @@ +# Documentation + +- [Installation](./01-Installation.md) +- [Validation Strategies](./02-ValidationStrategies.md) +- [IO](./03-IO.md) +- [Basic Usage](./04-BasicUsage.md) +- [Async Validation](./05-AsyncValidation.md) +- [Collections](./06-Collections.md) +- [Dependent Fields](./07-DependentFields.md) +- [Form Submission](./08-FormSubmission.md) +- [I18n](./09-I18n.md) +- [Caveats](./10-Caveats.md) +- [API](./11-API.md) diff --git a/examples/FormsWithFieldId.re b/examples/FormsWithFieldId.re deleted file mode 100644 index b532710a..00000000 --- a/examples/FormsWithFieldId.re +++ /dev/null @@ -1,58 +0,0 @@ -module LoginForm = { - type field = - | Email - | Password - | RememberMe; - - type state = { - email: string, - password: string, - rememberMe: bool, - }; - - type message = string; - type submissionError = unit; - - let validators = []; -}; - -module LoginFormNoId = Formality.Make(LoginForm); - -module LoginFormWithId = - Formality.MakeWithId({ - include LoginForm; - module FieldId = - Id.MakeComparable({ - type t = field; - let cmp = Pervasives.compare; - }); - }); - -module LoginFormAsyncNoId = - Formality.Async.Make({ - include LoginForm; - let debounceInterval = 100; - }); - -module LoginFormAsyncId = - Formality.Async.MakeWithId({ - include LoginForm; - let debounceInterval = 100; - module FieldId = - Id.MakeComparable({ - type t = field; - let cmp = Pervasives.compare; - }); - }); - -module LoginFormAsyncBlurNoId = Formality.Async.MakeOnBlur(LoginForm); - -module LoginFormAsyncBlurId = - Formality.Async.MakeOnBlurWithId({ - include LoginForm; - module FieldId = - Id.MakeComparable({ - type t = field; - let cmp = Pervasives.compare; - }); - }); diff --git a/examples/LoginForm.re b/examples/LoginForm.re deleted file mode 100644 index 2973a8c7..00000000 --- a/examples/LoginForm.re +++ /dev/null @@ -1,185 +0,0 @@ -module LoginForm = { - open Formality; - - type field = - | Email - | Password - | RememberMe; - - type state = { - email: string, - password: string, - rememberMe: bool, - }; - - type message = string; - type submissionError = unit; - - module EmailField = { - let update = (state, value) => {...state, email: value}; - - let validator = { - field: Email, - strategy: Strategy.OnFirstSuccessOrFirstBlur, - dependents: None, - validate: ({email}) => { - let emailRegex = [%bs.re {|/.*@.*\..+/|}]; - switch (email) { - | "" => Error("Email is required") - | _ as value when !emailRegex->Js.Re.test_(value) => - Error("Email is invalid") - | _ => Ok(Valid) - }; - }, - }; - }; - - module PasswordField = { - let update = (state, value) => {...state, password: value}; - - let validator = { - field: Password, - strategy: Strategy.OnFirstBlur, - dependents: None, - validate: ({password}) => - switch (password) { - | "" => Error("Password is required") - | _ => Ok(Valid) - }, - }; - }; - - module RememberMeField = { - let update = (state, value) => {...state, rememberMe: value}; - }; - - let validators = [EmailField.validator, PasswordField.validator]; -}; - -module LoginFormHook = Formality.Make(LoginForm); - -let initialState = LoginForm.{email: "", password: "", rememberMe: false}; - -[@react.component] -let make = () => { - let form = - LoginFormHook.useForm( - ~initialState, - ~onSubmit=(state, form) => { - Js.log2("Submitted with:", state); - Js.Global.setTimeout( - () => { - form.notifyOnSuccess(None); - form.reset->Js.Global.setTimeout(3000)->ignore; - }, - 500, - ) - ->ignore; - }, - ); - -
Formality.Dom.preventDefault}> -
-
-

"Login"->React.string

-
- - form.blur(Email)} - onChange={event => - form.change( - Email, - LoginForm.EmailField.update( - form.state, - event->ReactEvent.Form.target##value, - ), - ) - } - /> - {switch (Email->(form.result)) { - | Some(Error(message)) => -
- message->React.string -
- | Some(Ok(Valid)) => -
- {j|✓|j}->React.string -
- | Some(Ok(NoValue)) - | None => React.null - }} -
-
- - form.blur(Password)} - onChange={event => - form.change( - Password, - LoginForm.PasswordField.update( - form.state, - event->ReactEvent.Form.target##value, - ), - ) - } - /> - {switch (Password->(form.result)) { - | Some(Error(message)) => -
- message->React.string -
- | Some(Ok(Valid)) => -
- {j|✓|j}->React.string -
- | Some(Ok(NoValue)) - | None => React.null - }} -
-
- form.blur(RememberMe)} - onChange={event => - form.change( - RememberMe, - LoginForm.RememberMeField.update( - form.state, - event->ReactEvent.Form.target##checked, - ), - ) - } - /> - -
-
- - {switch (form.status) { - | Submitted => -
- {j|✓ Logged In|j}->React.string -
- | _ => React.null - }} -
-
- ; -}; diff --git a/examples/SignupForm.re b/examples/SignupForm.re deleted file mode 100644 index e021cd69..00000000 --- a/examples/SignupForm.re +++ /dev/null @@ -1,251 +0,0 @@ -module SignupForm = { - open Formality; - - type field = - | Email - | Password - | PasswordConfirmation; - - type state = { - email: string, - password: string, - passwordConfirmation: string, - }; - - type message = string; - type submissionError = unit; - - let debounceInterval = Formality.Async.debounceInterval; - - module EmailField = { - let update = (state, value) => {...state, email: value}; - - let validator = - Async.{ - field: Email, - strategy: OnFirstSuccessOrFirstBlur, - dependents: None, - validate: ({email}) => { - let emailRegex = [%bs.re {|/.*@.*\..+/|}]; - switch (email) { - | "" => Error("Email is required") - | _ as value when !emailRegex->Js.Re.test_(value) => - Error("Email is invalid") - | _ => Ok(Valid) - }; - }, - validateAsync: - Some(( - state => - Js.Promise.( - state.email - ->Api.validateEmail - ->then_( - valid => - Result.( - valid - ? Ok(Valid)->resolve - : Error("Email is already taken")->resolve - ), - _, - ) - ), - (prev, next) => prev.email == next.email, - )), - }; - }; - - module PasswordField = { - let update = (state, value) => {...state, password: value}; - - let validator = - Async.{ - field: Password, - strategy: OnFirstSuccessOrFirstBlur, - dependents: [PasswordConfirmation]->Some, - validate: ({password}) => { - let minLength = 4; - switch (password) { - | "" => Error("Password is required") - | _ when password->Js.String.length < minLength => - Error({j| $(minLength)+ characters, please|j}) - | _ => Ok(Valid) - }; - }, - validateAsync: None, - }; - }; - - module PasswordConfirmationField = { - let update = (state, value) => {...state, passwordConfirmation: value}; - - let validator = - Async.{ - field: PasswordConfirmation, - strategy: Strategy.OnFirstSuccessOrFirstBlur, - dependents: None, - validate: ({password, passwordConfirmation}) => - switch (passwordConfirmation) { - | "" => Error("Confirmation is required") - | _ when passwordConfirmation !== password => - Error("Password doesn't match") - | _ => Ok(Valid) - }, - validateAsync: None, - }; - }; - - let validators = [ - EmailField.validator, - PasswordField.validator, - PasswordConfirmationField.validator, - ]; -}; - -module SignupFormHook = Formality.Async.Make(SignupForm); - -let initialState = - SignupForm.{email: "", password: "", passwordConfirmation: ""}; - -[@react.component] -let make = () => { - let form = - SignupFormHook.useForm( - ~initialState, - ~onSubmit=(state, form) => { - Js.log2("Submitted with:", state); - Js.Global.setTimeout( - () => { - form.notifyOnSuccess(None); - form.reset->Js.Global.setTimeout(3000)->ignore; - }, - 500, - ) - ->ignore; - }, - ); - -
Formality.Dom.preventDefault}> -
-
-

"Signup"->React.string

-
- - form.blur(Email)} - onChange={event => - form.change( - Email, - SignupForm.EmailField.update( - form.state, - event->ReactEvent.Form.target##value, - ), - ) - } - /> - {switch (Email->(form.result), Email->(form.validating)) { - | (_, true) => -
"Checking..."->React.string
- | (Some(Error(message)), false) => -
- message->React.string -
- | (Some(Ok(Valid)), false) => -
- {j|✓|j}->React.string -
- | (Some(Ok(NoValue)) | None, false) => React.null - }} -
-
-
- "Hint: try `test@taken.email`"->React.string -
-
-
- - form.blur(Password)} - onChange={event => - form.change( - Password, - SignupForm.PasswordField.update( - form.state, - event->ReactEvent.Form.target##value, - ), - ) - } - /> - {switch (Password->(form.result)) { - | Some(Error(message)) => -
- message->React.string -
- | Some(Ok(Valid)) => -
- {j|✓|j}->React.string -
- | Some(Ok(NoValue)) - | None => React.null - }} -
-
- - form.blur(PasswordConfirmation)} - onChange={event => - form.change( - PasswordConfirmation, - SignupForm.PasswordConfirmationField.update( - form.state, - event->ReactEvent.Form.target##value, - ), - ) - } - /> - {switch (PasswordConfirmation->(form.result)) { - | Some(Error(message)) => -
- message->React.string -
- | Some(Ok(Valid)) => -
- {j|✓|j}->React.string -
- | Some(Ok(NoValue)) - | None => React.null - }} -
-
- - {switch (form.status) { - | Submitted => -
- {j|✓ Signed Up|j}->React.string -
- | _ => React.null - }} -
-
- ; -}; diff --git a/examples/bsconfig.json b/examples/bsconfig.json new file mode 100644 index 00000000..cd88648b --- /dev/null +++ b/examples/bsconfig.json @@ -0,0 +1,20 @@ +{ + "name": "re-formality-examples", + "sources": ["src"], + "bs-dependencies": [ + "reason-react", + "re-formality", + "re-classnames" + ], + "reason": { + "react-jsx": 3 + }, + "refmt": 3, + "bsc-flags": ["-open Belt"], + "ppx-flags": ["../lib/_build/default/bin/bin.exe"], + "package-specs": { + "module": "es6", + "in-source": true + }, + "suffix": ".bs.js" +} diff --git a/examples/index.css b/examples/index.css deleted file mode 100644 index 96b606ad..00000000 --- a/examples/index.css +++ /dev/null @@ -1,213 +0,0 @@ -@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro:300'); - -:root { - --font-size: 18px; -} - -* { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -html, -body, -#app { - width: 100%; - height: 100%; - background-color: #eaeaea; - font-family: "Source Sans Pro", sans-serif; - font-weight: 300; - font-size: 20px; -} - -#app { - display: flex; - flex-flow: column nowrap; - align-items: center; - flex: 1; - min-height: 100%; - padding: 20px 40px; -} - -.container { - display: flex; - flex-flow: column nowrap; - width: 800px; - max-width: 800px; -} - -.header { - display: flex; - flex-flow: row nowrap; - align-items: center; - justify-content: space-between; -} - -.link { - color: inherit; - text-decoration: none; -} - -.link:active, -.link:focus, -.link:hover { - outline: none; - text-decoration: underline; -} - -.nav { - display: flex; - flex-flow: row nowrap; - margin-bottom: 10px; -} - -.nav-link { - margin-right: 10px; - background-color: transparent; -} - -.nav-link.active { - border-bottom: 1px solid #29d; -} - -h1, -h2 { - margin: 0 0 22px; -} - -.form { - position: relative; - background-color: #fff; - padding: 30px 40px 40px; -} - -.form-messages-area { - position: absolute; - top: 0; - right: 0; - bottom: 0; - background-color: #f9f9f9; - border-left: 1px dashed #ccc; -} - -.form-messages-area-md { - width: calc(800px - 40px - 100px - 15px - 300px - 40px); -} - -.form-messages-area-lg { - width: calc(800px - 40px - 130px - 15px - 300px - 40px); -} - -.form-content { - position: relative; -} - -.form-row { - display: flex; - flex-flow: row nowrap; - justify-content: flex-start; - align-items: center; - margin-top: 20px; -} - -.form-row.form-row-footer { - margin-top: 5px; -} - -label { - display: inline-flex; - justify-content: flex-end; - margin-right: 15px; - cursor: pointer; - font-size: var(--font-size); -} - -.label-md { - width: 100px; -} - -.push-md { - margin-left: calc(100px + 15px); -} - -.label-lg { - width: 130px; -} - -.push-lg { - margin-left: calc(130px + 15px); -} - -input, -button { - transition: border-color 100ms ease-in; - font-size: var(--font-size); - font-weight: 300; -} - -input[type="text"] { - display: inline-flex; - border: 1px solid #ccc; - padding: 8px; - font-size: var(--font-size); - width: 300px; -} - -label, -input { - margin-right: 15px; -} - -button { - display: inline-flex; - padding: 10px 20px; - border: 1px solid #ccc; - border-radius: 4px; - color: #333; -} - -input:focus, -button:focus, -button:hover { - outline: none; - border: 1px solid #29d; -} - -button:active { - transform: translateY(1px); -} - -input:disabled, -button:disabled { - pointer-events: none; -} - -.note { - margin-top: 7px; - font-size: 14px; - color: #777; -} - -.form-message, -.form-status { - font-size: 14px; -} - -.form-message { - margin-left: 40px; -} - -.form-status { - margin-left: 20px; -} - -.form-message.success, -.form-status.success { - color: green; -} - -.form-message.failure, -.form-status.failure { - color: red; -} diff --git a/examples/package.json b/examples/package.json new file mode 100644 index 00000000..2d579c5b --- /dev/null +++ b/examples/package.json @@ -0,0 +1,29 @@ +{ + "name": "re-formality-examples", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "parcel src/index.html", + "build": "parcel build src/index.html", + "prebuild": "yarn run clean && yarn run bs:build", + "clean": "yarn run dist:clean && yarn run bs:clean", + "bs:build": "bsb -clean-world -make-world", + "bs:watch": "bsb -clean-world -make-world -w", + "bs:clean": "bsb -clean-world", + "dist:clean": "rm -rf dist", + "format": "bsrefmt --in-place **/*.{re,rei}", + "test": "exit 0", + "deploy": "now deploy dist --public --name re-formality", + "predeploy": "yarn run build" + }, + "dependencies": { + "re-classnames": "4.1.0", + "re-formality": "*", + "reason-react": "0.7.0" + }, + "devDependencies": { + "bs-platform": "7.2.2", + "bsb-js": "1.1.7", + "parcel-bundler": "1.12.4" + } +} diff --git a/examples/Api.re b/examples/src/Api.re similarity index 100% rename from examples/Api.re rename to examples/src/Api.re diff --git a/examples/App.re b/examples/src/App.re similarity index 73% rename from examples/App.re rename to examples/src/App.re index 773fa030..8de0da19 100644 --- a/examples/App.re +++ b/examples/src/App.re @@ -1,12 +1,14 @@ module Route = { type t = | Signup - | Login; + | Login + | BlogPost; let fromUrl = (url: ReasonReactRouter.url) => switch (url.hash) { | "signup" => Signup | "login" => Login + | "blog-post" => BlogPost | _ => Signup }; }; @@ -25,6 +27,7 @@ let make = () => {
+
{switch (route) { | Signup => | Login => + | BlogPost => }}
; }; diff --git a/examples/src/BlogPostForm.re b/examples/src/BlogPostForm.re new file mode 100644 index 00000000..3fd5b570 --- /dev/null +++ b/examples/src/BlogPostForm.re @@ -0,0 +1,212 @@ +module BlogPostForm = [%form + type input = { + title: string, + authors: [@field.collection] array(author), + } + and author = {name: [@field.deps author.name] string}; + let validators = { + title: { + strategy: OnFirstSuccessOrFirstBlur, + validate: ({title}) => { + switch (title) { + | "" => Error("Title is required") + | _ => Ok(title) + }; + }, + }, + authors: { + collection: input => + switch (input.authors) { + | [||] => Error("There must be at least one author") + | _ => Ok() + }, + fields: { + name: { + strategy: OnFirstSuccessOrFirstBlur, + validate: ({authors}, ~at) => { + switch (authors->Array.getUnsafe(at)) { + | {name: ""} => Error("Author name is required") + | {name} + when + authors->Js.Array2.somei((author, idx) => + if (at == idx) { + false; + } else { + author.name == name; + } + ) => + Error("Author name must be uniq") + | {name} => Ok(name) + }; + }, + }, + }, + }, + } +]; + +let initialInput: BlogPostForm.input = {title: "", authors: [|{name: ""}|]}; + +[@react.component] +let make = () => { + let form = + BlogPostForm.useForm( + ~initialInput, + ~onSubmit=(output, form) => { + Js.log2("Submitted with:", output); + Js.Global.setTimeout( + () => { + form.notifyOnSuccess(None); + form.reset->Js.Global.setTimeout(3000)->ignore; + }, + 500, + ) + ->ignore; + }, + ); + +
+
+
+

"Blog post"->React.string

+
+ + form.blurTitle()} + onChange={event => { + let value = event->ReactEvent.Form.target##value; + form.updateTitle(input => {...input, title: value}); + }} + /> + {switch (form.titleResult) { + | Some(Error(message)) => +
+ message->React.string +
+ | Some(Ok(_)) => +
+ {j|✓|j}->React.string +
+ | None => React.null + }} +
+
+

"Authors"->React.string

+ {switch (form.authorsResult) { + | Some(Error(message)) => +
+ message->React.string +
+ | Some(Ok ()) + | None => React.null + }} +
+ {form.input.authors + ->Array.mapWithIndex((index, author) => { + let authorNameDomId = + "blog-post--author-name" ++ index->Int.toString; + +
+ + form.blurAuthorName(~at=index)} + onChange={event => { + let value = event->ReactEvent.Form.target##value; + form.updateAuthorName(~at=index, input => + { + ...input, + authors: + form.input.authors + ->Array.mapWithIndex((idx, author) => + if (idx != index) { + author; + } else { + {name: value}; + } + ), + } + ); + }} + /> + + {switch (form.authorNameResult(~at=index)) { + | Some(Error(message)) => +
+ message->React.string +
+ | Some(Ok(_)) => +
+ {j|✓|j}->React.string +
+ | None => React.null + }} +
; + }) + ->React.array} +
+ +
+
+ + {switch (form.status) { + | Submitted => +
+ {j|✓ Posted|j}->React.string +
+ | _ => React.null + }} +
+
+ ; +}; diff --git a/examples/src/Form.re b/examples/src/Form.re new file mode 100644 index 00000000..c5188265 --- /dev/null +++ b/examples/src/Form.re @@ -0,0 +1,13 @@ +[@react.component] +let make = (~className, ~onSubmit, ~children) => { +
{ + if (!event->ReactEvent.Form.defaultPrevented) { + event->ReactEvent.Form.preventDefault; + }; + onSubmit(); + }}> + children +
; +}; diff --git a/examples/src/LoginForm.re b/examples/src/LoginForm.re new file mode 100644 index 00000000..5f53dfe3 --- /dev/null +++ b/examples/src/LoginForm.re @@ -0,0 +1,165 @@ +module LoginForm = [%form + type input = { + email: string, + password: string, + rememberMe: bool, + }; + let validators = { + email: { + strategy: OnFirstSuccessOrFirstBlur, + validate: ({email}) => { + let emailRegex = [%bs.re {|/.*@.*\..+/|}]; + switch (email) { + | "" => Error("Email is required") + | _ as value when !emailRegex->Js.Re.test_(value) => + Error("Email is invalid") + | _ => Ok(email) + }; + }, + }, + password: { + strategy: OnFirstBlur, + validate: ({password}) => + switch (password) { + | "" => Error("Password is required") + | _ => Ok(password) + }, + }, + rememberMe: None, + } +]; + +let initialInput: LoginForm.input = { + email: "", + password: "", + rememberMe: false, +}; + +[@react.component] +let make = () => { + let form = + LoginForm.useForm( + ~initialInput, + ~onSubmit=(output, form) => { + Js.log2("Submitted with:", output); + Js.Global.setTimeout( + () => { + form.notifyOnSuccess(None); + form.reset->Js.Global.setTimeout(3000)->ignore; + }, + 500, + ) + ->ignore; + }, + ); + +
+
+
+

"Login"->React.string

+
+ + form.blurEmail()} + onChange={event => { + let value = event->ReactEvent.Form.target##value; + form.updateEmail(input => {...input, email: value}); + }} + /> + {switch (form.emailResult) { + | Some(Error(message)) => +
+ message->React.string +
+ | Some(Ok(_)) => +
+ {j|✓|j}->React.string +
+ | None => React.null + }} +
+
+ + form.blurPassword()} + onChange={event => { + let value = event->ReactEvent.Form.target##value; + form.updatePassword(input => {...input, password: value}); + }} + /> + {switch (form.passwordResult) { + | Some(Error(message)) => +
+ message->React.string +
+ | Some(Ok(_)) => +
+ {j|✓|j}->React.string +
+ | None => React.null + }} +
+
+ form.blurRememberMe()} + onChange={event => { + let checked = event->ReactEvent.Form.target##checked; + form.updateRememberMe(input => {...input, rememberMe: checked}); + }} + /> + +
+
+ + {switch (form.status) { + | Submitted => +
+ {j|✓ Logged In|j}->React.string +
+ | _ => React.null + }} +
+
+ ; +}; diff --git a/examples/src/SignupForm.re b/examples/src/SignupForm.re new file mode 100644 index 00000000..0d524f1a --- /dev/null +++ b/examples/src/SignupForm.re @@ -0,0 +1,224 @@ +module SignupForm = [%form + type input = { + email: [@field.async] string, + password: [@field.deps passwordConfirmation] string, + passwordConfirmation: string, + }; + let validators = { + email: { + strategy: OnFirstSuccessOrFirstBlur, + validate: ({email}) => { + let emailRegex = [%bs.re {|/.*@.*\..+/|}]; + switch (email) { + | "" => Error("Email is required") + | _ as value when !emailRegex->Js.Re.test_(value) => + Error("Email is invalid") + | _ => Ok(email) + }; + }, + validateAsync: email => + Js.Promise.( + email + ->Api.validateEmail + ->then_( + valid => + valid + ? Ok(email)->resolve + : Error("Email is already taken")->resolve, + _, + ) + ), + }, + password: { + strategy: OnFirstSuccessOrFirstBlur, + validate: ({password}) => { + let minLength = 4; + switch (password) { + | "" => Error("Password is required") + | _ when password->Js.String.length < minLength => + Error({j| $(minLength)+ characters, please|j}) + | _ => Ok(password) + }; + }, + }, + passwordConfirmation: { + strategy: OnFirstSuccessOrFirstBlur, + validate: ({password, passwordConfirmation}) => + switch (passwordConfirmation) { + | "" => Error("Confirmation is required") + | _ when passwordConfirmation !== password => + Error("Password doesn't match") + | _ => Ok(passwordConfirmation) + }, + }, + } +]; + +let initialInput: SignupForm.input = { + email: "", + password: "", + passwordConfirmation: "", +}; + +[@react.component] +let make = () => { + let form = + SignupForm.useForm( + ~initialInput, + ~onSubmit=(output, form) => { + Js.log2("Submitted with:", output); + Js.Global.setTimeout( + () => { + form.notifyOnSuccess(None); + form.reset->Js.Global.setTimeout(3000)->ignore; + }, + 500, + ) + ->ignore; + }, + ); + +
+
+
+

"Signup"->React.string

+
+ + form.blurEmail()} + onChange={event => { + let value = event->ReactEvent.Form.target##value; + form.updateEmail(input => {...input, email: value}); + }} + /> + {switch (form.emailResult) { + | Some(Validating(_)) => +
+ "Checking..."->React.string +
+ | Some(Result(Error(message))) => +
+ message->React.string +
+ | Some(Result(Ok(_))) => +
+ {j|✓|j}->React.string +
+ | None => React.null + }} +
+
+
+ "Hint: try `test@taken.email`"->React.string +
+
+
+ + form.blurPassword()} + onChange={event => { + let value = event->ReactEvent.Form.target##value; + form.updatePassword(input => {...input, password: value}); + }} + /> + {switch (form.passwordResult) { + | Some(Error(message)) => +
+ message->React.string +
+ | Some(Ok(_)) => +
+ {j|✓|j}->React.string +
+ | None => React.null + }} +
+
+ + form.blurPasswordConfirmation()} + onChange={event => { + let value = event->ReactEvent.Form.target##value; + form.updatePasswordConfirmation(input => + {...input, passwordConfirmation: value} + ); + }} + /> + {switch (form.passwordConfirmationResult) { + | Some(Error(message)) => +
+ message->React.string +
+ | Some(Ok(_)) => +
+ {j|✓|j}->React.string +
+ | None => React.null + }} +
+
+ + {switch (form.status) { + | Submitted => +
+ {j|✓ Signed Up|j}->React.string +
+ | _ => React.null + }} +
+
+ ; +}; diff --git a/examples/src/index.css b/examples/src/index.css new file mode 100644 index 00000000..1c719fab --- /dev/null +++ b/examples/src/index.css @@ -0,0 +1,276 @@ +@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,600'); + +:root { + --font-family: "Source Sans Pro", sans-serif; + --font-size: 20px; + + --bg-color: #eaeaea; + --active-color: #29d; + --border-color: #ccc; + + --container-width: 900px; + --form-padding-top: 30px; + --form-padding-bottom: 40px; + --form-padding-left: 40px; + --form-padding-right: 40px; + --label-width-md: 100px; + --label-width-lg: 130px; + --label-margin-right: 15px; + --input-margin-right: 15px; + --input-width: 350px; + --control-width: 20px; + --message-margin-left: 20px; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html, +body, +#app { + width: 100%; + height: 100%; + background-color: var(--bg-color); + font-family: var(--font-family); + font-weight: 300; + font-size: var(--font-size); +} + +#app { + display: flex; + flex-flow: column nowrap; + align-items: center; + flex: 1; + min-height: 100%; + padding: 20px 40px; +} + +.container { + display: flex; + flex-flow: column nowrap; + width: var(--container-width); + max-width: var(--container-width); +} + +.header { + display: flex; + flex-flow: row nowrap; + align-items: center; + justify-content: space-between; +} + +.link { + color: inherit; + text-decoration: none; +} + +.link:active, +.link:focus, +.link:hover { + outline: none; + text-decoration: underline; +} + +.nav { + display: flex; + flex-flow: row nowrap; + margin-bottom: 10px; +} + +.nav-link { + margin-right: 10px; + background-color: transparent; +} + +.nav-link.active { + border-bottom: 1px solid var(--active-color); +} + +h1, +h2 { + margin: 0 0 22px; +} + +h3 { + margin: 0 var(--input-margin-right) 0 0; + width: var(--input-width); + font-weight: 600; + font-size: 1em; +} + +.form { + position: relative; + background-color: #fff; + padding: var(--form-padding-top) var(--form-padding-right) var(--form-padding-bottom) var(--form-padding-left); +} + +.form-messages-area { + position: absolute; + top: 0; + right: 0; + bottom: 0; + background-color: #f9f9f9; + border-left: 1px dashed var(--border-color); +} + +.form-messages-area-md { + width: calc(var(--container-width) - var(--form-padding-left) - var(--label-width-md) - var(--label-margin-right) - var(--input-width) - var(--input-margin-right) - var(--control-width) - var(--input-margin-right)); +} + +.form-messages-area-lg { + width: calc(var(--container-width) - var(--form-padding-left) - var(--label-width-lg) - var(--label-margin-right) - var(--input-width) - var(--input-margin-right) - var(--control-width) - var(--input-margin-right)); +} + +.form-content { + position: relative; +} + +.form-row { + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: center; + margin-top: 20px; +} + +.form-row.form-row-footer { + margin-top: 5px; +} + +label { + display: inline-flex; + justify-content: flex-end; + margin-right: var(--label-margin-right); + cursor: pointer; + font-size: var(--font-size); +} + +.label-md { + width: var(--label-width-md); +} + +.push-md { + margin-left: calc(var(--label-width-md) + var(--label-margin-right)); +} + +.label-lg { + width: var(--label-width-lg); +} + +.push-lg { + margin-left: calc(var(--label-width-lg) + var(--label-margin-right)); +} + +input, +button { + transition: border-color 100ms ease-in; + font-size: var(--font-size); + font-weight: 300; +} + +input[type="text"] { + display: inline-flex; + border: 1px solid var(--border-color); + padding: 8px; + font-size: var(--font-size); + width: var(--input-width); +} + +label { + margin-right: var(--label-margin-right); +} + +input { + margin-right: var(--input-margin-right); +} + +button { + display: inline-flex; + justify-content: center; + color: #333; +} + +button.primary, +button.secondary { + border: 1px solid var(--border-color); + border-radius: 4px; +} + +button.primary { + padding: 10px 20px; + font-size: 1em; +} + +button.secondary { + padding: 6px 10px; + font-size: 0.75em; +} + +button.control { + border: 0; + padding: 0; + width: var(--control-width); +} + +input:focus, +button.primary:focus, +button.secondary:focus, +button.primary:hover, +button.secondary:hover { + outline: none; + border: 1px solid var(--active-color); +} + +button.control:focus, +button.control:hover { + outline: none; +} + +button:active { + transform: translateY(1px); +} + +input:disabled, +button:disabled { + pointer-events: none; +} + +.note { + margin-top: 7px; + font-size: 14px; + color: #777; +} + +.form-message, +.form-status { + font-size: 14px; +} + +.form-message-for-field { + margin-left: calc(var(--control-width) + var(--input-margin-right) + var(--message-margin-left)); +} + +.form-message-for-collection { + margin-left: calc(var(--control-width) + var(--input-margin-right) + var(--message-margin-left)); +} + +.form-message-for-field-of-collection { + margin-left: calc(var(--input-margin-right) + var(--message-margin-left)); +} + +.form-status { + margin-left: 20px; +} + +.form-message.success, +.form-status.success { + color: green; +} + +.form-message.failure, +.form-status.failure { + color: red; +} diff --git a/examples/index.html b/examples/src/index.html similarity index 100% rename from examples/index.html rename to examples/src/index.html diff --git a/examples/index.re b/examples/src/index.re similarity index 100% rename from examples/index.re rename to examples/src/index.re diff --git a/lib/bin/Bin.re b/lib/bin/Bin.re new file mode 100644 index 00000000..25d3caf0 --- /dev/null +++ b/lib/bin/Bin.re @@ -0,0 +1 @@ +Ppxlib.Driver.run_as_ppx_rewriter(); diff --git a/lib/bin/dune b/lib/bin/dune new file mode 100644 index 00000000..0be22c04 --- /dev/null +++ b/lib/bin/dune @@ -0,0 +1,5 @@ +(executable + (name bin) + (public_name re-formality-ppx) + (libraries re-formality-ppx.lib) +) diff --git a/bsconfig.json b/lib/bsconfig.json similarity index 53% rename from bsconfig.json rename to lib/bsconfig.json index 9d4d83eb..c5289ebd 100644 --- a/bsconfig.json +++ b/lib/bsconfig.json @@ -1,18 +1,10 @@ { "name": "re-formality", - "sources": [ - "src", - { - "dir": "examples", - "type" : "dev", - "subdirs": true - } - ], + "sources": ["src"], "bs-dependencies": [ "reason-react", "re-debouncer" ], - "bs-dev-dependencies": ["re-classnames"], "reason": { "react-jsx": 3 }, @@ -22,8 +14,5 @@ "module": "es6", "in-source": true }, - "suffix": ".bs.js", - "warnings": { - "number": "-45" - } + "suffix": ".bs.js" } diff --git a/lib/dune-project b/lib/dune-project new file mode 100644 index 00000000..1a46f0ea --- /dev/null +++ b/lib/dune-project @@ -0,0 +1,2 @@ +(lang dune 1.6) + (name re-formality-ppx) diff --git a/lib/esy.json b/lib/esy.json new file mode 100644 index 00000000..168fc403 --- /dev/null +++ b/lib/esy.json @@ -0,0 +1,22 @@ +{ + "name": "re-formality-ppx", + "version": "1.1.0", + "description": "Form validation tool for reason-react", + "author": "Alex Fedoseev ", + "license": "MIT", + "esy": { + "build": "dune build -p #{self.name}", + "buildsInSource": "_build" + }, + "dependencies": { + "@esy-ocaml/reason": "*", + "@opam/dune": "2.0.1", + "@opam/ppxlib": "0.12.0", + "ocaml": "~4.6.1000", + "refmterr": "*" + }, + "devDependencies": { + "@opam/merlin": "*", + "@opam/alcotest": "1.0.1" + } +} diff --git a/lib/esy.lock/.gitattributes b/lib/esy.lock/.gitattributes new file mode 100644 index 00000000..e0b4e26c --- /dev/null +++ b/lib/esy.lock/.gitattributes @@ -0,0 +1,3 @@ + +# Set eol to LF so files aren't converted to CRLF-eol on Windows. +* text eol=lf linguist-generated diff --git a/lib/esy.lock/.gitignore b/lib/esy.lock/.gitignore new file mode 100644 index 00000000..a221be22 --- /dev/null +++ b/lib/esy.lock/.gitignore @@ -0,0 +1,3 @@ + +# Reset any possible .gitignore, we want all esy.lock to be un-ignored. +!* diff --git a/lib/esy.lock/index.json b/lib/esy.lock/index.json new file mode 100644 index 00000000..40ead349 --- /dev/null +++ b/lib/esy.lock/index.json @@ -0,0 +1,1142 @@ +{ + "checksum": "d47233a3f11b73fcd142940199ce3d8d", + "root": "re-formality-ppx@link-dev:./esy.json", + "node": { + "refmterr@3.3.0@d41d8cd9": { + "id": "refmterr@3.3.0@d41d8cd9", + "name": "refmterr", + "version": "3.3.0", + "source": { + "type": "install", + "source": [ + "archive:https://registry.npmjs.org/refmterr/-/refmterr-3.3.0.tgz#sha1:45adde80205093c201b491b3c37dd7740c9b036b" + ] + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@reason-native/pastel@0.3.0@d41d8cd9", + "@reason-native/console@0.1.0@d41d8cd9", + "@opam/re@opam:1.9.0@d4d5e13d", "@opam/dune@opam:2.0.1@5dc56bd0", + "@opam/atdgen@opam:2.0.0@46af0360", + "@esy-ocaml/reason@3.5.2@d41d8cd9" + ], + "devDependencies": [] + }, + "re-formality-ppx@link-dev:./esy.json": { + "id": "re-formality-ppx@link-dev:./esy.json", + "name": "re-formality-ppx", + "version": "link-dev:./esy.json", + "source": { "type": "link-dev", "path": ".", "manifest": "esy.json" }, + "overrides": [], + "dependencies": [ + "refmterr@3.3.0@d41d8cd9", "ocaml@4.6.1000@d41d8cd9", + "@opam/ppxlib@opam:0.12.0@fcf5cabc", + "@opam/dune@opam:2.0.1@5dc56bd0", "@esy-ocaml/reason@3.5.2@d41d8cd9" + ], + "devDependencies": [ + "@opam/merlin@opam:3.3.3@d653b06a", + "@opam/alcotest@opam:1.0.1@412cd081" + ] + }, + "ocaml@4.6.1000@d41d8cd9": { + "id": "ocaml@4.6.1000@d41d8cd9", + "name": "ocaml", + "version": "4.6.1000", + "source": { + "type": "install", + "source": [ + "archive:https://registry.npmjs.org/ocaml/-/ocaml-4.6.1000.tgz#sha1:99525ef559353481396454f9a072dedc96b52f44" + ] + }, + "overrides": [], + "dependencies": [], + "devDependencies": [] + }, + "@reason-native/pastel@0.3.0@d41d8cd9": { + "id": "@reason-native/pastel@0.3.0@d41d8cd9", + "name": "@reason-native/pastel", + "version": "0.3.0", + "source": { + "type": "install", + "source": [ + "archive:https://registry.npmjs.org/@reason-native/pastel/-/pastel-0.3.0.tgz#sha1:07da3c5a0933e61bc3b353bc85aa71ac7c0f311c" + ] + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/re@opam:1.9.0@d4d5e13d", + "@opam/dune@opam:2.0.1@5dc56bd0", "@esy-ocaml/reason@3.5.2@d41d8cd9" + ], + "devDependencies": [] + }, + "@reason-native/console@0.1.0@d41d8cd9": { + "id": "@reason-native/console@0.1.0@d41d8cd9", + "name": "@reason-native/console", + "version": "0.1.0", + "source": { + "type": "install", + "source": [ + "archive:https://registry.npmjs.org/@reason-native/console/-/console-0.1.0.tgz#sha1:3b56f0e9e1be8464329793df29020aa90e71c22c" + ] + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@esy-ocaml/reason@3.5.2@d41d8cd9" + ], + "devDependencies": [] + }, + "@opam/yojson@opam:1.7.0@7056d985": { + "id": "@opam/yojson@opam:1.7.0@7056d985", + "name": "@opam/yojson", + "version": "opam:1.7.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/b8/b89d39ca3f8c532abe5f547ad3b8f84d#md5:b89d39ca3f8c532abe5f547ad3b8f84d", + "archive:https://github.com/ocaml-community/yojson/releases/download/1.7.0/yojson-1.7.0.tbz#md5:b89d39ca3f8c532abe5f547ad3b8f84d" + ], + "opam": { + "name": "yojson", + "version": "1.7.0", + "path": "esy.lock/opam/yojson.1.7.0" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/easy-format@opam:1.3.2@0484b3c4", + "@opam/dune@opam:2.0.1@5dc56bd0", "@opam/cppo@opam:1.6.6@f4f83858", + "@opam/biniou@opam:1.2.1@d7570399", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/easy-format@opam:1.3.2@0484b3c4", + "@opam/dune@opam:2.0.1@5dc56bd0", "@opam/biniou@opam:1.2.1@d7570399" + ] + }, + "@opam/uuidm@opam:0.9.7@bf725775": { + "id": "@opam/uuidm@opam:0.9.7@bf725775", + "name": "@opam/uuidm", + "version": "opam:0.9.7", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/54/54658248e3981d8c05237d0a4277ccd3#md5:54658248e3981d8c05237d0a4277ccd3", + "archive:https://erratique.ch/software/uuidm/releases/uuidm-0.9.7.tbz#md5:54658248e3981d8c05237d0a4277ccd3" + ], + "opam": { + "name": "uuidm", + "version": "0.9.7", + "path": "esy.lock/opam/uuidm.0.9.7" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/topkg@opam:1.0.1@a42c631e", + "@opam/ocamlfind@opam:1.8.1@ff07b0f9", + "@opam/ocamlbuild@opam:0.14.0@6ac75d03", + "@opam/cmdliner@opam:1.0.4@93208aac", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ "ocaml@4.6.1000@d41d8cd9" ] + }, + "@opam/topkg@opam:1.0.1@a42c631e": { + "id": "@opam/topkg@opam:1.0.1@a42c631e", + "name": "@opam/topkg", + "version": "opam:1.0.1", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/16/16b90e066d8972a5ef59655e7c28b3e9#md5:16b90e066d8972a5ef59655e7c28b3e9", + "archive:http://erratique.ch/software/topkg/releases/topkg-1.0.1.tbz#md5:16b90e066d8972a5ef59655e7c28b3e9" + ], + "opam": { + "name": "topkg", + "version": "1.0.1", + "path": "esy.lock/opam/topkg.1.0.1" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/ocamlfind@opam:1.8.1@ff07b0f9", + "@opam/ocamlbuild@opam:0.14.0@6ac75d03", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/ocamlbuild@opam:0.14.0@6ac75d03" + ] + }, + "@opam/stdlib-shims@opam:0.1.0@d957c903": { + "id": "@opam/stdlib-shims@opam:0.1.0@d957c903", + "name": "@opam/stdlib-shims", + "version": "opam:0.1.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/12/12b5704eed70c6bff5ac39a16db1425d#md5:12b5704eed70c6bff5ac39a16db1425d", + "archive:https://github.com/ocaml/stdlib-shims/releases/download/0.1.0/stdlib-shims-0.1.0.tbz#md5:12b5704eed70c6bff5ac39a16db1425d" + ], + "opam": { + "name": "stdlib-shims", + "version": "0.1.0", + "path": "esy.lock/opam/stdlib-shims.0.1.0" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/stdio@opam:v0.13.0@eb59d879": { + "id": "@opam/stdio@opam:v0.13.0@eb59d879", + "name": "@opam/stdio", + "version": "opam:v0.13.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/48/48ef28512ddd51ff9885649dd1fab91d#md5:48ef28512ddd51ff9885649dd1fab91d", + "archive:https://ocaml.janestreet.com/ocaml-core/v0.13/files/stdio-v0.13.0.tar.gz#md5:48ef28512ddd51ff9885649dd1fab91d" + ], + "opam": { + "name": "stdio", + "version": "v0.13.0", + "path": "esy.lock/opam/stdio.v0.13.0" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@opam/base@opam:v0.13.1@7d937ed0", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@opam/base@opam:v0.13.1@7d937ed0" + ] + }, + "@opam/sexplib0@opam:v0.13.0@3f54c2be": { + "id": "@opam/sexplib0@opam:v0.13.0@3f54c2be", + "name": "@opam/sexplib0", + "version": "opam:v0.13.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/f8/f8a715dffda5599cfae0cb4031d57abe#md5:f8a715dffda5599cfae0cb4031d57abe", + "archive:https://ocaml.janestreet.com/ocaml-core/v0.13/files/sexplib0-v0.13.0.tar.gz#md5:f8a715dffda5599cfae0cb4031d57abe" + ], + "opam": { + "name": "sexplib0", + "version": "v0.13.0", + "path": "esy.lock/opam/sexplib0.v0.13.0" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/seq@opam:0.2.2@e9144e45": { + "id": "@opam/seq@opam:0.2.2@e9144e45", + "name": "@opam/seq", + "version": "opam:0.2.2", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/90/9033e02283aa3bde9f97f24e632902e3#md5:9033e02283aa3bde9f97f24e632902e3", + "archive:https://github.com/c-cube/seq/archive/0.2.2.tar.gz#md5:9033e02283aa3bde9f97f24e632902e3" + ], + "opam": { + "name": "seq", + "version": "0.2.2", + "path": "esy.lock/opam/seq.0.2.2" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/result@opam:1.5@6b753c82": { + "id": "@opam/result@opam:1.5@6b753c82", + "name": "@opam/result", + "version": "opam:1.5", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/1b/1b82dec78849680b49ae9a8a365b831b#md5:1b82dec78849680b49ae9a8a365b831b", + "archive:https://github.com/janestreet/result/releases/download/1.5/result-1.5.tbz#md5:1b82dec78849680b49ae9a8a365b831b" + ], + "opam": { + "name": "result", + "version": "1.5", + "path": "esy.lock/opam/result.1.5" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/re@opam:1.9.0@d4d5e13d": { + "id": "@opam/re@opam:1.9.0@d4d5e13d", + "name": "@opam/re", + "version": "opam:1.9.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/bd/bddaed4f386a22cace7850c9c7dac296#md5:bddaed4f386a22cace7850c9c7dac296", + "archive:https://github.com/ocaml/ocaml-re/releases/download/1.9.0/re-1.9.0.tbz#md5:bddaed4f386a22cace7850c9c7dac296" + ], + "opam": { + "name": "re", + "version": "1.9.0", + "path": "esy.lock/opam/re.1.9.0" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/seq@opam:0.2.2@e9144e45", + "@opam/dune@opam:2.0.1@5dc56bd0", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/seq@opam:0.2.2@e9144e45", + "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/ppxlib@opam:0.12.0@fcf5cabc": { + "id": "@opam/ppxlib@opam:0.12.0@fcf5cabc", + "name": "@opam/ppxlib", + "version": "opam:0.12.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/6b/6b562c9b3b9350777318729921f890850b385c469db60769aafd9371998a2c42#sha256:6b562c9b3b9350777318729921f890850b385c469db60769aafd9371998a2c42", + "archive:https://github.com/ocaml-ppx/ppxlib/archive/0.12.0.tar.gz#sha256:6b562c9b3b9350777318729921f890850b385c469db60769aafd9371998a2c42" + ], + "opam": { + "name": "ppxlib", + "version": "0.12.0", + "path": "esy.lock/opam/ppxlib.0.12.0" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/stdio@opam:v0.13.0@eb59d879", + "@opam/ppx_derivers@opam:1.2.1@ecf0aa45", + "@opam/ocaml-migrate-parsetree@opam:1.6.0@da2643e7", + "@opam/ocaml-compiler-libs@opam:v0.12.1@5c34eb0d", + "@opam/dune@opam:2.0.1@5dc56bd0", "@opam/base@opam:v0.13.1@7d937ed0", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/stdio@opam:v0.13.0@eb59d879", + "@opam/ppx_derivers@opam:1.2.1@ecf0aa45", + "@opam/ocaml-migrate-parsetree@opam:1.6.0@da2643e7", + "@opam/ocaml-compiler-libs@opam:v0.12.1@5c34eb0d", + "@opam/dune@opam:2.0.1@5dc56bd0", "@opam/base@opam:v0.13.1@7d937ed0" + ] + }, + "@opam/ppx_derivers@opam:1.2.1@ecf0aa45": { + "id": "@opam/ppx_derivers@opam:1.2.1@ecf0aa45", + "name": "@opam/ppx_derivers", + "version": "opam:1.2.1", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/5d/5dc2bf130c1db3c731fe0fffc5648b41#md5:5dc2bf130c1db3c731fe0fffc5648b41", + "archive:https://github.com/ocaml-ppx/ppx_derivers/archive/1.2.1.tar.gz#md5:5dc2bf130c1db3c731fe0fffc5648b41" + ], + "opam": { + "name": "ppx_derivers", + "version": "1.2.1", + "path": "esy.lock/opam/ppx_derivers.1.2.1" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/ocamlfind@opam:1.8.1@ff07b0f9": { + "id": "@opam/ocamlfind@opam:1.8.1@ff07b0f9", + "name": "@opam/ocamlfind", + "version": "opam:1.8.1", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/18/18ca650982c15536616dea0e422cbd8c#md5:18ca650982c15536616dea0e422cbd8c", + "archive:http://download2.camlcity.org/download/findlib-1.8.1.tar.gz#md5:18ca650982c15536616dea0e422cbd8c", + "archive:http://download.camlcity.org/download/findlib-1.8.1.tar.gz#md5:18ca650982c15536616dea0e422cbd8c" + ], + "opam": { + "name": "ocamlfind", + "version": "1.8.1", + "path": "esy.lock/opam/ocamlfind.1.8.1" + } + }, + "overrides": [ + { + "opamoverride": + "esy.lock/overrides/opam__s__ocamlfind_opam__c__1.8.1_opam_override" + } + ], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/conf-m4@opam:1@3b2b148a", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ "ocaml@4.6.1000@d41d8cd9" ] + }, + "@opam/ocamlbuild@opam:0.14.0@6ac75d03": { + "id": "@opam/ocamlbuild@opam:0.14.0@6ac75d03", + "name": "@opam/ocamlbuild", + "version": "opam:0.14.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/87/87b29ce96958096c0a1a8eeafeb6268077b2d11e1bf2b3de0f5ebc9cf8d42e78#sha256:87b29ce96958096c0a1a8eeafeb6268077b2d11e1bf2b3de0f5ebc9cf8d42e78", + "archive:https://github.com/ocaml/ocamlbuild/archive/0.14.0.tar.gz#sha256:87b29ce96958096c0a1a8eeafeb6268077b2d11e1bf2b3de0f5ebc9cf8d42e78" + ], + "opam": { + "name": "ocamlbuild", + "version": "0.14.0", + "path": "esy.lock/opam/ocamlbuild.0.14.0" + } + }, + "overrides": [ + { + "opamoverride": + "esy.lock/overrides/opam__s__ocamlbuild_opam__c__0.14.0_opam_override" + } + ], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ "ocaml@4.6.1000@d41d8cd9" ] + }, + "@opam/ocaml-migrate-parsetree@opam:1.6.0@da2643e7": { + "id": "@opam/ocaml-migrate-parsetree@opam:1.6.0@da2643e7", + "name": "@opam/ocaml-migrate-parsetree", + "version": "opam:1.6.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/9b/9b018e7d25114ce17fc0b82b7cd7c927b84ebb6b043aa987fa7731c2484de33f#sha256:9b018e7d25114ce17fc0b82b7cd7c927b84ebb6b043aa987fa7731c2484de33f", + "archive:https://github.com/ocaml-ppx/ocaml-migrate-parsetree/releases/download/v1.6.0/ocaml-migrate-parsetree-v1.6.0.tbz#sha256:9b018e7d25114ce17fc0b82b7cd7c927b84ebb6b043aa987fa7731c2484de33f" + ], + "opam": { + "name": "ocaml-migrate-parsetree", + "version": "1.6.0", + "path": "esy.lock/opam/ocaml-migrate-parsetree.1.6.0" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/result@opam:1.5@6b753c82", + "@opam/ppx_derivers@opam:1.2.1@ecf0aa45", + "@opam/dune@opam:2.0.1@5dc56bd0", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/result@opam:1.5@6b753c82", + "@opam/ppx_derivers@opam:1.2.1@ecf0aa45", + "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/ocaml-compiler-libs@opam:v0.12.1@5c34eb0d": { + "id": "@opam/ocaml-compiler-libs@opam:v0.12.1@5c34eb0d", + "name": "@opam/ocaml-compiler-libs", + "version": "opam:v0.12.1", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/2f/2f929af7c764a3f681a5671f271210c4#md5:2f929af7c764a3f681a5671f271210c4", + "archive:https://github.com/janestreet/ocaml-compiler-libs/archive/v0.12.1.tar.gz#md5:2f929af7c764a3f681a5671f271210c4" + ], + "opam": { + "name": "ocaml-compiler-libs", + "version": "v0.12.1", + "path": "esy.lock/opam/ocaml-compiler-libs.v0.12.1" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/merlin-extend@opam:0.5@a5dd7d4b": { + "id": "@opam/merlin-extend@opam:0.5@a5dd7d4b", + "name": "@opam/merlin-extend", + "version": "opam:0.5", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/ca/ca3a38c360c7d4827eb4789abf7a6aa4b6e3b4e3c3ef69a5be64dce4601ec227#sha256:ca3a38c360c7d4827eb4789abf7a6aa4b6e3b4e3c3ef69a5be64dce4601ec227", + "archive:https://github.com/let-def/merlin-extend/releases/download/v0.5/merlin-extend-v0.5.tbz#sha256:ca3a38c360c7d4827eb4789abf7a6aa4b6e3b4e3c3ef69a5be64dce4601ec227" + ], + "opam": { + "name": "merlin-extend", + "version": "0.5", + "path": "esy.lock/opam/merlin-extend.0.5" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@opam/cppo@opam:1.6.6@f4f83858", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/merlin@opam:3.3.3@d653b06a": { + "id": "@opam/merlin@opam:3.3.3@d653b06a", + "name": "@opam/merlin", + "version": "opam:3.3.3", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/72/72909ef47eea1f6fca13b4109a34dccf8fe3923a3c026f1ed1db9eb5ee9aae15#sha256:72909ef47eea1f6fca13b4109a34dccf8fe3923a3c026f1ed1db9eb5ee9aae15", + "archive:https://github.com/ocaml/merlin/releases/download/v3.3.3/merlin-v3.3.3.tbz#sha256:72909ef47eea1f6fca13b4109a34dccf8fe3923a3c026f1ed1db9eb5ee9aae15" + ], + "opam": { + "name": "merlin", + "version": "3.3.3", + "path": "esy.lock/opam/merlin.3.3.3" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/yojson@opam:1.7.0@7056d985", + "@opam/ocamlfind@opam:1.8.1@ff07b0f9", + "@opam/dune@opam:2.0.1@5dc56bd0", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/yojson@opam:1.7.0@7056d985", + "@opam/ocamlfind@opam:1.8.1@ff07b0f9", + "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/menhirSdk@opam:20200211@1b43927c": { + "id": "@opam/menhirSdk@opam:20200211@1b43927c", + "name": "@opam/menhirSdk", + "version": "opam:20200211", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/01/01577e5f15380c35bdaa8fd818204560#md5:01577e5f15380c35bdaa8fd818204560", + "archive:https://gitlab.inria.fr/fpottier/menhir/repository/20200211/archive.tar.gz#md5:01577e5f15380c35bdaa8fd818204560" + ], + "opam": { + "name": "menhirSdk", + "version": "20200211", + "path": "esy.lock/opam/menhirSdk.20200211" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/menhirLib@opam:20200211@99279102": { + "id": "@opam/menhirLib@opam:20200211@99279102", + "name": "@opam/menhirLib", + "version": "opam:20200211", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/01/01577e5f15380c35bdaa8fd818204560#md5:01577e5f15380c35bdaa8fd818204560", + "archive:https://gitlab.inria.fr/fpottier/menhir/repository/20200211/archive.tar.gz#md5:01577e5f15380c35bdaa8fd818204560" + ], + "opam": { + "name": "menhirLib", + "version": "20200211", + "path": "esy.lock/opam/menhirLib.20200211" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/menhir@opam:20200123@fdbafd0c": { + "id": "@opam/menhir@opam:20200123@fdbafd0c", + "name": "@opam/menhir", + "version": "opam:20200123", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/91/91aeae45fbf781e82ec3fe636be6ad49#md5:91aeae45fbf781e82ec3fe636be6ad49", + "archive:https://gitlab.inria.fr/fpottier/menhir/repository/20200123/archive.tar.gz#md5:91aeae45fbf781e82ec3fe636be6ad49" + ], + "opam": { + "name": "menhir", + "version": "20200123", + "path": "esy.lock/opam/menhir.20200123" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/menhirSdk@opam:20200211@1b43927c", + "@opam/menhirLib@opam:20200211@99279102", + "@opam/dune@opam:2.0.1@5dc56bd0", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/menhirSdk@opam:20200211@1b43927c", + "@opam/menhirLib@opam:20200211@99279102", + "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/jbuilder@opam:1.0+beta20.2@053ddcf2": { + "id": "@opam/jbuilder@opam:1.0+beta20.2@053ddcf2", + "name": "@opam/jbuilder", + "version": "opam:1.0+beta20.2", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/fb/fbe8c3b1facb206cac3fb8932b5dd5d9#md5:fbe8c3b1facb206cac3fb8932b5dd5d9", + "archive:https://github.com/ocaml/dune/releases/download/1.0%2Bbeta20.2/jbuilder-1.0+beta20.2.tbz#md5:fbe8c3b1facb206cac3fb8932b5dd5d9" + ], + "opam": { + "name": "jbuilder", + "version": "1.0+beta20.2", + "path": "esy.lock/opam/jbuilder.1.0+beta20.2" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ "ocaml@4.6.1000@d41d8cd9" ] + }, + "@opam/fmt@opam:0.8.8@01c3a23c": { + "id": "@opam/fmt@opam:0.8.8@01c3a23c", + "name": "@opam/fmt", + "version": "opam:0.8.8", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/47/473490fcfdf3ff0a8ccee226b873d4b2#md5:473490fcfdf3ff0a8ccee226b873d4b2", + "archive:https://erratique.ch/software/fmt/releases/fmt-0.8.8.tbz#md5:473490fcfdf3ff0a8ccee226b873d4b2" + ], + "opam": { + "name": "fmt", + "version": "0.8.8", + "path": "esy.lock/opam/fmt.0.8.8" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/topkg@opam:1.0.1@a42c631e", + "@opam/stdlib-shims@opam:0.1.0@d957c903", + "@opam/seq@opam:0.2.2@e9144e45", + "@opam/ocamlfind@opam:1.8.1@ff07b0f9", + "@opam/ocamlbuild@opam:0.14.0@6ac75d03", + "@opam/cmdliner@opam:1.0.4@93208aac", + "@opam/base-unix@opam:base@87d0b2eb", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/stdlib-shims@opam:0.1.0@d957c903", + "@opam/seq@opam:0.2.2@e9144e45" + ] + }, + "@opam/easy-format@opam:1.3.2@0484b3c4": { + "id": "@opam/easy-format@opam:1.3.2@0484b3c4", + "name": "@opam/easy-format", + "version": "opam:1.3.2", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/34/3440c2b882d537ae5e9011eb06abb53f5667e651ea4bb3b460ea8230fa8c1926#sha256:3440c2b882d537ae5e9011eb06abb53f5667e651ea4bb3b460ea8230fa8c1926", + "archive:https://github.com/mjambon/easy-format/releases/download/1.3.2/easy-format-1.3.2.tbz#sha256:3440c2b882d537ae5e9011eb06abb53f5667e651ea4bb3b460ea8230fa8c1926" + ], + "opam": { + "name": "easy-format", + "version": "1.3.2", + "path": "esy.lock/opam/easy-format.1.3.2" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/dune-private-libs@opam:2.0.1@d5d5e717": { + "id": "@opam/dune-private-libs@opam:2.0.1@d5d5e717", + "name": "@opam/dune-private-libs", + "version": "opam:2.0.1", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/e0/e04090c846f005f1cc02c390e963a7efe74c653ce2c5c7fd2e7e30a06ceadcb7#sha256:e04090c846f005f1cc02c390e963a7efe74c653ce2c5c7fd2e7e30a06ceadcb7", + "archive:https://github.com/ocaml/dune/releases/download/2.0.1/dune-2.0.1.tbz#sha256:e04090c846f005f1cc02c390e963a7efe74c653ce2c5c7fd2e7e30a06ceadcb7" + ], + "opam": { + "name": "dune-private-libs", + "version": "2.0.1", + "path": "esy.lock/opam/dune-private-libs.2.0.1" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/dune-configurator@opam:2.2.0@95249fb0": { + "id": "@opam/dune-configurator@opam:2.2.0@95249fb0", + "name": "@opam/dune-configurator", + "version": "opam:2.2.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/ab/ab5eb970c21641a6f3034448218cdc1c138512b906c7d2f67fea41ecd98d8bf4#sha256:ab5eb970c21641a6f3034448218cdc1c138512b906c7d2f67fea41ecd98d8bf4", + "archive:https://github.com/ocaml/dune/releases/download/2.2.0/dune-2.2.0.tbz#sha256:ab5eb970c21641a6f3034448218cdc1c138512b906c7d2f67fea41ecd98d8bf4" + ], + "opam": { + "name": "dune-configurator", + "version": "2.2.0", + "path": "esy.lock/opam/dune-configurator.2.2.0" + } + }, + "overrides": [], + "dependencies": [ + "@opam/dune-private-libs@opam:2.0.1@d5d5e717", + "@opam/dune@opam:2.0.1@5dc56bd0", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "@opam/dune-private-libs@opam:2.0.1@d5d5e717", + "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/dune@opam:2.0.1@5dc56bd0": { + "id": "@opam/dune@opam:2.0.1@5dc56bd0", + "name": "@opam/dune", + "version": "opam:2.0.1", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/e0/e04090c846f005f1cc02c390e963a7efe74c653ce2c5c7fd2e7e30a06ceadcb7#sha256:e04090c846f005f1cc02c390e963a7efe74c653ce2c5c7fd2e7e30a06ceadcb7", + "archive:https://github.com/ocaml/dune/releases/download/2.0.1/dune-2.0.1.tbz#sha256:e04090c846f005f1cc02c390e963a7efe74c653ce2c5c7fd2e7e30a06ceadcb7" + ], + "opam": { + "name": "dune", + "version": "2.0.1", + "path": "esy.lock/opam/dune.2.0.1" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/base-unix@opam:base@87d0b2eb", + "@opam/base-threads@opam:base@36803084", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/base-unix@opam:base@87d0b2eb", + "@opam/base-threads@opam:base@36803084" + ] + }, + "@opam/cppo@opam:1.6.6@f4f83858": { + "id": "@opam/cppo@opam:1.6.6@f4f83858", + "name": "@opam/cppo", + "version": "opam:1.6.6", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/e7/e7272996a7789175b87bb998efd079794a8db6625aae990d73f7b4484a07b8a0#sha256:e7272996a7789175b87bb998efd079794a8db6625aae990d73f7b4484a07b8a0", + "archive:https://github.com/ocaml-community/cppo/releases/download/v1.6.6/cppo-v1.6.6.tbz#sha256:e7272996a7789175b87bb998efd079794a8db6625aae990d73f7b4484a07b8a0" + ], + "opam": { + "name": "cppo", + "version": "1.6.6", + "path": "esy.lock/opam/cppo.1.6.6" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@opam/base-unix@opam:base@87d0b2eb", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/dune@opam:2.0.1@5dc56bd0", + "@opam/base-unix@opam:base@87d0b2eb" + ] + }, + "@opam/conf-m4@opam:1@3b2b148a": { + "id": "@opam/conf-m4@opam:1@3b2b148a", + "name": "@opam/conf-m4", + "version": "opam:1", + "source": { + "type": "install", + "source": [ "no-source:" ], + "opam": { + "name": "conf-m4", + "version": "1", + "path": "esy.lock/opam/conf-m4.1" + } + }, + "overrides": [], + "dependencies": [ "@esy-ocaml/substs@0.0.1@d41d8cd9" ], + "devDependencies": [] + }, + "@opam/cmdliner@opam:1.0.4@93208aac": { + "id": "@opam/cmdliner@opam:1.0.4@93208aac", + "name": "@opam/cmdliner", + "version": "opam:1.0.4", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/fe/fe2213d0bc63b1e10a2d0aa66d2fc8d9#md5:fe2213d0bc63b1e10a2d0aa66d2fc8d9", + "archive:http://erratique.ch/software/cmdliner/releases/cmdliner-1.0.4.tbz#md5:fe2213d0bc63b1e10a2d0aa66d2fc8d9" + ], + "opam": { + "name": "cmdliner", + "version": "1.0.4", + "path": "esy.lock/opam/cmdliner.1.0.4" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ "ocaml@4.6.1000@d41d8cd9" ] + }, + "@opam/biniou@opam:1.2.1@d7570399": { + "id": "@opam/biniou@opam:1.2.1@d7570399", + "name": "@opam/biniou", + "version": "opam:1.2.1", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/35/35546c68b1929a8e6d27a3b39ecd17b38303a0d47e65eb9d1480c2061ea84335#sha256:35546c68b1929a8e6d27a3b39ecd17b38303a0d47e65eb9d1480c2061ea84335", + "archive:https://github.com/mjambon/biniou/releases/download/1.2.1/biniou-1.2.1.tbz#sha256:35546c68b1929a8e6d27a3b39ecd17b38303a0d47e65eb9d1480c2061ea84335" + ], + "opam": { + "name": "biniou", + "version": "1.2.1", + "path": "esy.lock/opam/biniou.1.2.1" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/easy-format@opam:1.3.2@0484b3c4", + "@opam/dune@opam:2.0.1@5dc56bd0", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/easy-format@opam:1.3.2@0484b3c4", + "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/base-unix@opam:base@87d0b2eb": { + "id": "@opam/base-unix@opam:base@87d0b2eb", + "name": "@opam/base-unix", + "version": "opam:base", + "source": { + "type": "install", + "source": [ "no-source:" ], + "opam": { + "name": "base-unix", + "version": "base", + "path": "esy.lock/opam/base-unix.base" + } + }, + "overrides": [], + "dependencies": [ "@esy-ocaml/substs@0.0.1@d41d8cd9" ], + "devDependencies": [] + }, + "@opam/base-threads@opam:base@36803084": { + "id": "@opam/base-threads@opam:base@36803084", + "name": "@opam/base-threads", + "version": "opam:base", + "source": { + "type": "install", + "source": [ "no-source:" ], + "opam": { + "name": "base-threads", + "version": "base", + "path": "esy.lock/opam/base-threads.base" + } + }, + "overrides": [], + "dependencies": [ "@esy-ocaml/substs@0.0.1@d41d8cd9" ], + "devDependencies": [] + }, + "@opam/base-bytes@opam:base@19d0c2ff": { + "id": "@opam/base-bytes@opam:base@19d0c2ff", + "name": "@opam/base-bytes", + "version": "opam:base", + "source": { + "type": "install", + "source": [ "no-source:" ], + "opam": { + "name": "base-bytes", + "version": "base", + "path": "esy.lock/opam/base-bytes.base" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/ocamlfind@opam:1.8.1@ff07b0f9", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/ocamlfind@opam:1.8.1@ff07b0f9" + ] + }, + "@opam/base@opam:v0.13.1@7d937ed0": { + "id": "@opam/base@opam:v0.13.1@7d937ed0", + "name": "@opam/base", + "version": "opam:v0.13.1", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/29/296457416f9a8b75e6edfc3b1140e384#md5:296457416f9a8b75e6edfc3b1140e384", + "archive:https://github.com/janestreet/base/archive/v0.13.1.tar.gz#md5:296457416f9a8b75e6edfc3b1140e384" + ], + "opam": { + "name": "base", + "version": "v0.13.1", + "path": "esy.lock/opam/base.v0.13.1" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/sexplib0@opam:v0.13.0@3f54c2be", + "@opam/dune-configurator@opam:2.2.0@95249fb0", + "@opam/dune@opam:2.0.1@5dc56bd0", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/sexplib0@opam:v0.13.0@3f54c2be", + "@opam/dune-configurator@opam:2.2.0@95249fb0", + "@opam/dune@opam:2.0.1@5dc56bd0" + ] + }, + "@opam/atdgen-runtime@opam:2.0.0@60f6faab": { + "id": "@opam/atdgen-runtime@opam:2.0.0@60f6faab", + "name": "@opam/atdgen-runtime", + "version": "opam:2.0.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/14/14e47609397c524ea0eae7c3f14f7ccf#md5:14e47609397c524ea0eae7c3f14f7ccf", + "archive:https://github.com/mjambon/atd/releases/download/2.0.0/atd-2.0.0.tbz#md5:14e47609397c524ea0eae7c3f14f7ccf" + ], + "opam": { + "name": "atdgen-runtime", + "version": "2.0.0", + "path": "esy.lock/opam/atdgen-runtime.2.0.0" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/yojson@opam:1.7.0@7056d985", + "@opam/jbuilder@opam:1.0+beta20.2@053ddcf2", + "@opam/biniou@opam:1.2.1@d7570399", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/yojson@opam:1.7.0@7056d985", + "@opam/jbuilder@opam:1.0+beta20.2@053ddcf2", + "@opam/biniou@opam:1.2.1@d7570399" + ] + }, + "@opam/atdgen@opam:2.0.0@46af0360": { + "id": "@opam/atdgen@opam:2.0.0@46af0360", + "name": "@opam/atdgen", + "version": "opam:2.0.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/14/14e47609397c524ea0eae7c3f14f7ccf#md5:14e47609397c524ea0eae7c3f14f7ccf", + "archive:https://github.com/mjambon/atd/releases/download/2.0.0/atd-2.0.0.tbz#md5:14e47609397c524ea0eae7c3f14f7ccf" + ], + "opam": { + "name": "atdgen", + "version": "2.0.0", + "path": "esy.lock/opam/atdgen.2.0.0" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/yojson@opam:1.7.0@7056d985", + "@opam/jbuilder@opam:1.0+beta20.2@053ddcf2", + "@opam/biniou@opam:1.2.1@d7570399", + "@opam/atdgen-runtime@opam:2.0.0@60f6faab", + "@opam/atd@opam:2.0.0@e0ddd12f", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/yojson@opam:1.7.0@7056d985", + "@opam/jbuilder@opam:1.0+beta20.2@053ddcf2", + "@opam/biniou@opam:1.2.1@d7570399", + "@opam/atdgen-runtime@opam:2.0.0@60f6faab", + "@opam/atd@opam:2.0.0@e0ddd12f" + ] + }, + "@opam/atd@opam:2.0.0@e0ddd12f": { + "id": "@opam/atd@opam:2.0.0@e0ddd12f", + "name": "@opam/atd", + "version": "opam:2.0.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/14/14e47609397c524ea0eae7c3f14f7ccf#md5:14e47609397c524ea0eae7c3f14f7ccf", + "archive:https://github.com/mjambon/atd/releases/download/2.0.0/atd-2.0.0.tbz#md5:14e47609397c524ea0eae7c3f14f7ccf" + ], + "opam": { + "name": "atd", + "version": "2.0.0", + "path": "esy.lock/opam/atd.2.0.0" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/menhir@opam:20200123@fdbafd0c", + "@opam/jbuilder@opam:1.0+beta20.2@053ddcf2", + "@opam/easy-format@opam:1.3.2@0484b3c4", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", + "@opam/jbuilder@opam:1.0+beta20.2@053ddcf2", + "@opam/easy-format@opam:1.3.2@0484b3c4" + ] + }, + "@opam/astring@opam:0.8.3@4e5e17d5": { + "id": "@opam/astring@opam:0.8.3@4e5e17d5", + "name": "@opam/astring", + "version": "opam:0.8.3", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/md5/c5/c5bf6352b9ac27fbeab342740f4fa870#md5:c5bf6352b9ac27fbeab342740f4fa870", + "archive:http://erratique.ch/software/astring/releases/astring-0.8.3.tbz#md5:c5bf6352b9ac27fbeab342740f4fa870" + ], + "opam": { + "name": "astring", + "version": "0.8.3", + "path": "esy.lock/opam/astring.0.8.3" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/topkg@opam:1.0.1@a42c631e", + "@opam/ocamlfind@opam:1.8.1@ff07b0f9", + "@opam/ocamlbuild@opam:0.14.0@6ac75d03", + "@opam/base-bytes@opam:base@19d0c2ff", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/base-bytes@opam:base@19d0c2ff" + ] + }, + "@opam/alcotest@opam:1.0.1@412cd081": { + "id": "@opam/alcotest@opam:1.0.1@412cd081", + "name": "@opam/alcotest", + "version": "opam:1.0.1", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/0c/0c8748838a89df6dee4850aa7ef5e46c573265a9bf1589dec255bd8156a793f6#sha256:0c8748838a89df6dee4850aa7ef5e46c573265a9bf1589dec255bd8156a793f6", + "archive:https://github.com/mirage/alcotest/releases/download/1.0.1/alcotest-1.0.1.tbz#sha256:0c8748838a89df6dee4850aa7ef5e46c573265a9bf1589dec255bd8156a793f6" + ], + "opam": { + "name": "alcotest", + "version": "1.0.1", + "path": "esy.lock/opam/alcotest.1.0.1" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/uuidm@opam:0.9.7@bf725775", + "@opam/stdlib-shims@opam:0.1.0@d957c903", + "@opam/re@opam:1.9.0@d4d5e13d", "@opam/fmt@opam:0.8.8@01c3a23c", + "@opam/dune@opam:2.0.1@5dc56bd0", + "@opam/cmdliner@opam:1.0.4@93208aac", + "@opam/astring@opam:0.8.3@4e5e17d5", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/uuidm@opam:0.9.7@bf725775", + "@opam/stdlib-shims@opam:0.1.0@d957c903", + "@opam/re@opam:1.9.0@d4d5e13d", "@opam/fmt@opam:0.8.8@01c3a23c", + "@opam/dune@opam:2.0.1@5dc56bd0", + "@opam/cmdliner@opam:1.0.4@93208aac", + "@opam/astring@opam:0.8.3@4e5e17d5" + ] + }, + "@esy-ocaml/substs@0.0.1@d41d8cd9": { + "id": "@esy-ocaml/substs@0.0.1@d41d8cd9", + "name": "@esy-ocaml/substs", + "version": "0.0.1", + "source": { + "type": "install", + "source": [ + "archive:https://registry.npmjs.org/@esy-ocaml/substs/-/substs-0.0.1.tgz#sha1:59ebdbbaedcda123fc7ed8fb2b302b7d819e9a46" + ] + }, + "overrides": [], + "dependencies": [], + "devDependencies": [] + }, + "@esy-ocaml/reason@3.5.2@d41d8cd9": { + "id": "@esy-ocaml/reason@3.5.2@d41d8cd9", + "name": "@esy-ocaml/reason", + "version": "3.5.2", + "source": { + "type": "install", + "source": [ + "archive:https://registry.npmjs.org/@esy-ocaml/reason/-/reason-3.5.2.tgz#sha1:ac48b63fd66fbbc1d77ab6a2b7e3a1ba21a8f40b" + ] + }, + "overrides": [], + "dependencies": [ + "ocaml@4.6.1000@d41d8cd9", "@opam/result@opam:1.5@6b753c82", + "@opam/ocamlfind@opam:1.8.1@ff07b0f9", + "@opam/ocaml-migrate-parsetree@opam:1.6.0@da2643e7", + "@opam/merlin-extend@opam:0.5@a5dd7d4b", + "@opam/menhir@opam:20200123@fdbafd0c", + "@opam/dune@opam:2.0.1@5dc56bd0" + ], + "devDependencies": [] + } + } +} \ No newline at end of file diff --git a/lib/esy.lock/opam/alcotest.1.0.1/opam b/lib/esy.lock/opam/alcotest.1.0.1/opam new file mode 100644 index 00000000..cf487042 --- /dev/null +++ b/lib/esy.lock/opam/alcotest.1.0.1/opam @@ -0,0 +1,47 @@ +opam-version: "2.0" +maintainer: "thomas@gazagnaire.org" +authors: "Thomas Gazagnaire" +homepage: "https://github.com/mirage/alcotest/" +dev-repo: "git+https://github.com/mirage/alcotest.git" +bug-reports: "https://github.com/mirage/alcotest/issues/" +license: "ISC" +doc: "https://mirage.github.io/alcotest/" + +build: [ + ["dune" "subst"] {pinned} + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} +] + +depends: [ + "dune" {>= "1.11"} + "ocaml" {>= "4.03.0"} + "fmt" {>= "0.8.6"} + "astring" + "cmdliner" + "uuidm" + "re" + "stdlib-shims" +] + +synopsis: "Alcotest is a lightweight and colourful test framework" + +description: """ +Alcotest exposes simple interface to perform unit tests. It exposes +a simple TESTABLE module type, a check function to assert test +predicates and a run function to perform a list of unit -> unit +test callbacks. + +Alcotest provides a quiet and colorful output where only faulty runs +are fully displayed at the end of the run (with the full logs ready to +inspect), with a simple (yet expressive) query language to select the +tests to run. +""" +url { + src: + "https://github.com/mirage/alcotest/releases/download/1.0.1/alcotest-1.0.1.tbz" + checksum: [ + "sha256=0c8748838a89df6dee4850aa7ef5e46c573265a9bf1589dec255bd8156a793f6" + "sha512=f5f52dea5bb143e7001b8d0ac6131f8851389b080f46b9ad1ccacb95cc31a38143dd7122ccba59bb190abe559dbf81f33cc4dc3401ed95772d59be75fa566f19" + ] +} diff --git a/lib/esy.lock/opam/astring.0.8.3/opam b/lib/esy.lock/opam/astring.0.8.3/opam new file mode 100644 index 00000000..578ba1fa --- /dev/null +++ b/lib/esy.lock/opam/astring.0.8.3/opam @@ -0,0 +1,38 @@ +opam-version: "2.0" +maintainer: "Daniel Bünzli " +authors: ["Daniel Bünzli "] +homepage: "http://erratique.ch/software/astring" +doc: "http://erratique.ch/software/astring/doc" +dev-repo: "git+http://erratique.ch/repos/astring.git" +bug-reports: "https://github.com/dbuenzli/astring/issues" +tags: [ "string" "org:erratique" ] +license: "ISC" +depends: [ + "ocaml" {>= "4.01.0"} + "ocamlfind" {build} + "ocamlbuild" {build} + "topkg" {build} + "base-bytes" +] +build: [[ + "ocaml" "pkg/pkg.ml" "build" + "--pinned" "%{pinned}%" ]] +synopsis: "Alternative String module for OCaml" +description: """ +Astring exposes an alternative `String` module for OCaml. This module +tries to balance minimality and expressiveness for basic, index-free, +string processing and provides types and functions for substrings, +string sets and string maps. + +Remaining compatible with the OCaml `String` module is a non-goal. The +`String` module exposed by Astring has exception safe functions, +removes deprecated and rarely used functions, alters some signatures +and names, adds a few missing functions and fully exploits OCaml's +newfound string immutability. + +Astring depends only on the OCaml standard library. It is distributed +under the ISC license.""" +url { + src: "http://erratique.ch/software/astring/releases/astring-0.8.3.tbz" + checksum: "md5=c5bf6352b9ac27fbeab342740f4fa870" +} diff --git a/lib/esy.lock/opam/atd.2.0.0/opam b/lib/esy.lock/opam/atd.2.0.0/opam new file mode 100644 index 00000000..2808354c --- /dev/null +++ b/lib/esy.lock/opam/atd.2.0.0/opam @@ -0,0 +1,34 @@ +opam-version: "2.0" +maintainer: "martin@mjambon.com" +authors: ["Martin Jambon"] + +homepage: "https://github.com/mjambon/atd" +bug-reports: "https://github.com/mjambon/atd/issues" +dev-repo: "git://github.com/mjambon/atd.git" + +build: [ + ["jbuilder" "subst" "-p" name] {pinned} + ["jbuilder" "build" "-p" name "-j" jobs] +] + +# Restore when https://github.com/mjambon/atd/issues/121 is resolved. +# build-test: [ +# ["jbuilder" "runtest" "-p" name] +# ] + +depends: [ + "ocaml" {>= "4.03.0"} + "jbuilder" + "menhir" {build} + "easy-format" +] +synopsis: "Parser for the ATD data format description language" +description: """ +ATD is the OCaml library providing a parser for the ATD language and +various utilities. ATD stands for Adjustable Type Definitions in +reference to its main property of supporting annotations that allow a +good fit with a variety of data formats.""" +url { + src: "https://github.com/mjambon/atd/releases/download/2.0.0/atd-2.0.0.tbz" + checksum: "md5=14e47609397c524ea0eae7c3f14f7ccf" +} diff --git a/lib/esy.lock/opam/atdgen-runtime.2.0.0/opam b/lib/esy.lock/opam/atdgen-runtime.2.0.0/opam new file mode 100644 index 00000000..7236d930 --- /dev/null +++ b/lib/esy.lock/opam/atdgen-runtime.2.0.0/opam @@ -0,0 +1,29 @@ +opam-version: "2.0" +maintainer: "martin@mjambon.com" +authors: ["Martin Jambon"] + +homepage: "https://github.com/mjambon/atd" +bug-reports: "https://github.com/mjambon/atd/issues" +dev-repo: "git://github.com/mjambon/atd.git" + +build: [ + ["jbuilder" "subst" "-p" name] {pinned} + ["jbuilder" "build" "-p" name "-j" jobs] +] + +# Restore when https://github.com/mjambon/atd/issues/121 is resolved. +# build-test: [ +# ["jbuilder" "runtest" "-p" name] +# ] + +depends: [ + "ocaml" {>= "4.02.3"} + "jbuilder" + "biniou" {>= "1.0.6"} + "yojson" {>= "1.2.1"} +] +synopsis: "Runtime library for code generated by atdgen." +url { + src: "https://github.com/mjambon/atd/releases/download/2.0.0/atd-2.0.0.tbz" + checksum: "md5=14e47609397c524ea0eae7c3f14f7ccf" +} diff --git a/lib/esy.lock/opam/atdgen.2.0.0/opam b/lib/esy.lock/opam/atdgen.2.0.0/opam new file mode 100644 index 00000000..d71d304e --- /dev/null +++ b/lib/esy.lock/opam/atdgen.2.0.0/opam @@ -0,0 +1,44 @@ +opam-version: "2.0" +maintainer: "martin@mjambon.com" +authors: ["Martin Jambon"] + +homepage: "https://github.com/mjambon/atd" +bug-reports: "https://github.com/mjambon/atd/issues" +dev-repo: "git://github.com/mjambon/atd.git" + +build: [ + ["jbuilder" "subst" "-p" name] {pinned} + ["jbuilder" "build" "-p" name "-j" jobs] +] + +# Restore when https://github.com/mjambon/atd/issues/121 is resolved. +# build-test: [ +# ["jbuilder" "runtest" "-p" name] +# ] + +depends: [ + "ocaml" {>= "4.03.0"} + "jbuilder" + "atd" {>= "2.0.0"} + "atdgen-runtime" {>= "2.0.0"} + "biniou" {>= "1.0.6"} + "yojson" {>= "1.2.1"} +] +synopsis: + "Generates efficient JSON serializers, deserializers and validators" +description: """ +Atdgen is a command-line program that takes as input type definitions in the +ATD syntax and produces OCaml code suitable for data serialization and +deserialization. + +Two data formats are currently supported, these are biniou and JSON. +Atdgen-biniou and Atdgen-json will refer to Atdgen used in one context or the +other. + +Atdgen was designed with efficiency and durability in mind. Software authors +are encouraged to use Atdgen directly and to write tools that may reuse part of +Atdgen’s source code.""" +url { + src: "https://github.com/mjambon/atd/releases/download/2.0.0/atd-2.0.0.tbz" + checksum: "md5=14e47609397c524ea0eae7c3f14f7ccf" +} diff --git a/lib/esy.lock/opam/base-bytes.base/opam b/lib/esy.lock/opam/base-bytes.base/opam new file mode 100644 index 00000000..f1cae506 --- /dev/null +++ b/lib/esy.lock/opam/base-bytes.base/opam @@ -0,0 +1,9 @@ +opam-version: "2.0" +maintainer: " " +authors: " " +homepage: " " +depends: [ + "ocaml" {>= "4.02.0"} + "ocamlfind" {>= "1.5.3"} +] +synopsis: "Bytes library distributed with the OCaml compiler" diff --git a/lib/esy.lock/opam/base-threads.base/opam b/lib/esy.lock/opam/base-threads.base/opam new file mode 100644 index 00000000..914ff50c --- /dev/null +++ b/lib/esy.lock/opam/base-threads.base/opam @@ -0,0 +1,6 @@ +opam-version: "2.0" +maintainer: "https://github.com/ocaml/opam-repository/issues" +description: """ +Threads library distributed with the OCaml compiler +""" + diff --git a/lib/esy.lock/opam/base-unix.base/opam b/lib/esy.lock/opam/base-unix.base/opam new file mode 100644 index 00000000..b973540b --- /dev/null +++ b/lib/esy.lock/opam/base-unix.base/opam @@ -0,0 +1,6 @@ +opam-version: "2.0" +maintainer: "https://github.com/ocaml/opam-repository/issues" +description: """ +Unix library distributed with the OCaml compiler +""" + diff --git a/lib/esy.lock/opam/base.v0.13.1/opam b/lib/esy.lock/opam/base.v0.13.1/opam new file mode 100644 index 00000000..e3c61b28 --- /dev/null +++ b/lib/esy.lock/opam/base.v0.13.1/opam @@ -0,0 +1,36 @@ +opam-version: "2.0" +maintainer: "opensource@janestreet.com" +authors: ["Jane Street Group, LLC "] +homepage: "https://github.com/janestreet/base" +bug-reports: "https://github.com/janestreet/base/issues" +dev-repo: "git+https://github.com/janestreet/base.git" +doc: "https://ocaml.janestreet.com/ocaml-core/latest/doc/base/index.html" +license: "MIT" +build: [ + ["dune" "build" "-p" name "-j" jobs] +] +depends: [ + "ocaml" {>= "4.04.2"} + "sexplib0" {>= "v0.13" & < "v0.14"} + "dune" {>= "1.5.1"} + "dune-configurator" +] +synopsis: "Full standard library replacement for OCaml" +description: " +Full standard library replacement for OCaml + +Base is a complete and portable alternative to the OCaml standard +library. It provides all standard functionalities one would expect +from a language standard library. It uses consistent conventions +across all of its module. + +Base aims to be usable in any context. As a result system dependent +features such as I/O are not offered by Base. They are instead +provided by companion libraries such as stdio: + + https://github.com/janestreet/stdio +" +url { + src: "https://github.com/janestreet/base/archive/v0.13.1.tar.gz" + checksum: "md5=296457416f9a8b75e6edfc3b1140e384" +} diff --git a/lib/esy.lock/opam/biniou.1.2.1/opam b/lib/esy.lock/opam/biniou.1.2.1/opam new file mode 100644 index 00000000..b706b425 --- /dev/null +++ b/lib/esy.lock/opam/biniou.1.2.1/opam @@ -0,0 +1,45 @@ +opam-version: "2.0" +build: [ + ["dune" "subst"] {pinned} + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} + ["dune" "build" "-p" name "@doc"] {with-doc} +] +maintainer: ["martin@mjambon.com"] +authors: ["Martin Jambon"] +bug-reports: "https://github.com/mjambon/biniou/issues" +homepage: "https://github.com/mjambon/biniou" +doc: "https://mjambon.github.io/biniou/" +license: "BSD-3-Clause" +dev-repo: "git+https://github.com/mjambon/biniou.git" +synopsis: + "Binary data format designed for speed, safety, ease of use and backward compatibility as protocols evolve" +description: """ + +Biniou (pronounced "be new") is a binary data format designed for speed, safety, +ease of use and backward compatibility as protocols evolve. Biniou is vastly +equivalent to JSON in terms of functionality but allows implementations several +times faster (4 times faster than yojson), with 25-35% space savings. + +Biniou data can be decoded into human-readable form without knowledge of type +definitions except for field and variant names which are represented by 31-bit +hashes. A program named bdump is provided for routine visualization of biniou +data files. + +The program atdgen is used to derive OCaml-Biniou serializers and deserializers +from type definitions. + +Biniou format specification: mjambon.github.io/atdgen-doc/biniou-format.txt""" +depends: [ + "easy-format" + "dune" {>= "1.10"} + "ocaml" {>= "4.02.3"} +] +url { + src: + "https://github.com/mjambon/biniou/releases/download/1.2.1/biniou-1.2.1.tbz" + checksum: [ + "sha256=35546c68b1929a8e6d27a3b39ecd17b38303a0d47e65eb9d1480c2061ea84335" + "sha512=82670cc77bf3e869ee26e5fbe5a5affa45a22bc8b6c4bd7e85473912780e0111baca59b34a2c14feae3543ce6e239d7fddaeab24b686a65bfe642cdb91d27ebf" + ] +} diff --git a/lib/esy.lock/opam/cmdliner.1.0.4/opam b/lib/esy.lock/opam/cmdliner.1.0.4/opam new file mode 100644 index 00000000..b2187dc5 --- /dev/null +++ b/lib/esy.lock/opam/cmdliner.1.0.4/opam @@ -0,0 +1,36 @@ +opam-version: "2.0" +maintainer: "Daniel Bünzli " +authors: ["Daniel Bünzli "] +homepage: "http://erratique.ch/software/cmdliner" +doc: "http://erratique.ch/software/cmdliner/doc/Cmdliner" +dev-repo: "git+http://erratique.ch/repos/cmdliner.git" +bug-reports: "https://github.com/dbuenzli/cmdliner/issues" +tags: [ "cli" "system" "declarative" "org:erratique" ] +license: "ISC" +depends:[ "ocaml" {>= "4.03.0"} ] +build: [[ make "all" "PREFIX=%{prefix}%" ]] +install: +[[make "install" "LIBDIR=%{_:lib}%" "DOCDIR=%{_:doc}%" ] + [make "install-doc" "LIBDIR=%{_:lib}%" "DOCDIR=%{_:doc}%" ]] + +synopsis: """Declarative definition of command line interfaces for OCaml""" +description: """\ + +Cmdliner allows the declarative definition of command line interfaces +for OCaml. + +It provides a simple and compositional mechanism to convert command +line arguments to OCaml values and pass them to your functions. The +module automatically handles syntax errors, help messages and UNIX man +page generation. It supports programs with single or multiple commands +and respects most of the [POSIX][1] and [GNU][2] conventions. + +Cmdliner has no dependencies and is distributed under the ISC license. + +[1]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html +[2]: http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html +""" +url { +archive: "http://erratique.ch/software/cmdliner/releases/cmdliner-1.0.4.tbz" +checksum: "fe2213d0bc63b1e10a2d0aa66d2fc8d9" +} diff --git a/lib/esy.lock/opam/conf-m4.1/opam b/lib/esy.lock/opam/conf-m4.1/opam new file mode 100644 index 00000000..c6feb2a7 --- /dev/null +++ b/lib/esy.lock/opam/conf-m4.1/opam @@ -0,0 +1,22 @@ +opam-version: "2.0" +maintainer: "tim@gfxmonk.net" +homepage: "http://www.gnu.org/software/m4/m4.html" +bug-reports: "https://github.com/ocaml/opam-repository/issues" +authors: "GNU Project" +license: "GPL-3.0-only" +build: [["sh" "-exc" "echo | m4"]] +depexts: [ + ["m4"] {os-family = "debian"} + ["m4"] {os-distribution = "fedora"} + ["m4"] {os-distribution = "rhel"} + ["m4"] {os-distribution = "centos"} + ["m4"] {os-distribution = "alpine"} + ["m4"] {os-distribution = "nixos"} + ["m4"] {os-family = "suse"} + ["m4"] {os-distribution = "ol"} + ["m4"] {os-distribution = "arch"} +] +synopsis: "Virtual package relying on m4" +description: + "This package can only install if the m4 binary is installed on the system." +flags: conf diff --git a/lib/esy.lock/opam/cppo.1.6.6/opam b/lib/esy.lock/opam/cppo.1.6.6/opam new file mode 100644 index 00000000..f683f8b4 --- /dev/null +++ b/lib/esy.lock/opam/cppo.1.6.6/opam @@ -0,0 +1,37 @@ +opam-version: "2.0" +maintainer: "martin@mjambon.com" +authors: "Martin Jambon" +license: "BSD-3-Clause" +homepage: "http://mjambon.com/cppo.html" +doc: "https://ocaml-community.github.io/cppo/" +bug-reports: "https://github.com/ocaml-community/cppo/issues" +depends: [ + "ocaml" {>= "4.03"} + "dune" {>= "1.0"} + "base-unix" +] +build: [ + ["dune" "subst"] {pinned} + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} +] +dev-repo: "git+https://github.com/ocaml-community/cppo.git" +synopsis: "Code preprocessor like cpp for OCaml" +description: """ +Cppo is an equivalent of the C preprocessor for OCaml programs. +It allows the definition of simple macros and file inclusion. + +Cppo is: + +* more OCaml-friendly than cpp +* easy to learn without consulting a manual +* reasonably fast +* simple to install and to maintain +""" +url { + src: "https://github.com/ocaml-community/cppo/releases/download/v1.6.6/cppo-v1.6.6.tbz" + checksum: [ + "sha256=e7272996a7789175b87bb998efd079794a8db6625aae990d73f7b4484a07b8a0" + "sha512=44ecf9d225d9e45490a2feac0bde04865ca398dba6c3579e3370fcd1ea255707b8883590852af8b2df87123801062b9f3acce2455c092deabf431f9c4fb8d8eb" + ] +} diff --git a/lib/esy.lock/opam/dune-configurator.2.2.0/opam b/lib/esy.lock/opam/dune-configurator.2.2.0/opam new file mode 100644 index 00000000..309deca6 --- /dev/null +++ b/lib/esy.lock/opam/dune-configurator.2.2.0/opam @@ -0,0 +1,43 @@ +opam-version: "2.0" +synopsis: "Helper library for gathering system configuration" +description: """ +dune-configurator is a small library that helps writing OCaml scripts that +test features available on the system, in order to generate config.h +files for instance. +Among other things, dune-configurator allows one to: +- test if a C program compiles +- query pkg-config +- import #define from OCaml header files +- generate config.h file +""" +maintainer: ["Jane Street Group, LLC "] +authors: ["Jane Street Group, LLC "] +license: "MIT" +homepage: "https://github.com/ocaml/dune" +doc: "https://dune.readthedocs.io/" +bug-reports: "https://github.com/ocaml/dune/issues" +depends: [ + "dune" {>= "2.0"} + "dune-private-libs" {= version} +] +dev-repo: "git+https://github.com/ocaml/dune.git" +build: [ + ["dune" "subst"] {pinned} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@doc" {with-doc} + ] +] +url { + src: "https://github.com/ocaml/dune/releases/download/2.2.0/dune-2.2.0.tbz" + checksum: [ + "sha256=ab5eb970c21641a6f3034448218cdc1c138512b906c7d2f67fea41ecd98d8bf4" + "sha512=b48812a478ad1b75992a0c53dbdfac34841e3d5e97c559d63e2dd41d1724c21736cc3159743f3d95d85e1060ee5aa88a0abb46f5b0e778f533b40dead20af95c" + ] +} diff --git a/lib/esy.lock/opam/dune-private-libs.2.0.1/opam b/lib/esy.lock/opam/dune-private-libs.2.0.1/opam new file mode 100644 index 00000000..2cd658ab --- /dev/null +++ b/lib/esy.lock/opam/dune-private-libs.2.0.1/opam @@ -0,0 +1,42 @@ +opam-version: "2.0" +synopsis: "Private libraries of Dune" +description: """ +!!!!!!!!!!!!!!!!!!!!!! +!!!!! DO NOT USE !!!!! +!!!!!!!!!!!!!!!!!!!!!! + +This package contains code that is shared between various dune-xxx +packages. However, it is not meant for public consumption and provides +no stability guarantee. +""" +maintainer: ["Jane Street Group, LLC "] +authors: ["Jane Street Group, LLC "] +license: "MIT" +homepage: "https://github.com/ocaml/dune" +doc: "https://dune.readthedocs.io/" +bug-reports: "https://github.com/ocaml/dune/issues" +depends: [ + "dune" {>= "2.0"} + "ocaml" {>= "4.06"} +] +dev-repo: "git+https://github.com/ocaml/dune.git" +build: [ + ["dune" "subst"] {pinned} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@doc" {with-doc} + ] +] +url { + src: "https://github.com/ocaml/dune/releases/download/2.0.1/dune-2.0.1.tbz" + checksum: [ + "sha256=e04090c846f005f1cc02c390e963a7efe74c653ce2c5c7fd2e7e30a06ceadcb7" + "sha512=8c973ccfa1de0ff7173e17dac74ea850446a057866d47c7a100b271c7e440d5e607f1bfaa8fa5b756e0439492276e8c6615fac30cbff9ea900dc8e891f7ba4d3" + ] +} diff --git a/lib/esy.lock/opam/dune.2.0.1/opam b/lib/esy.lock/opam/dune.2.0.1/opam new file mode 100644 index 00000000..2bea38af --- /dev/null +++ b/lib/esy.lock/opam/dune.2.0.1/opam @@ -0,0 +1,51 @@ +opam-version: "2.0" +synopsis: "Fast, portable, and opinionated build system" +description: """ + +dune is a build system that was designed to simplify the release of +Jane Street packages. It reads metadata from "dune" files following a +very simple s-expression syntax. + +dune is fast, has very low-overhead, and supports parallel builds on +all platforms. It has no system dependencies; all you need to build +dune or packages using dune is OCaml. You don't need make or bash +as long as the packages themselves don't use bash explicitly. + +dune supports multi-package development by simply dropping multiple +repositories into the same directory. + +It also supports multi-context builds, such as building against +several opam roots/switches simultaneously. This helps maintaining +packages across several versions of OCaml and gives cross-compilation +for free. +""" +maintainer: ["Jane Street Group, LLC "] +authors: ["Jane Street Group, LLC "] +license: "MIT" +homepage: "https://github.com/ocaml/dune" +doc: "https://dune.readthedocs.io/" +bug-reports: "https://github.com/ocaml/dune/issues" +depends: [ + ("ocaml" {>= "4.06"} | ("ocaml" {< "4.06~~"} & "ocamlfind-secondary")) + "base-unix" + "base-threads" +] +conflicts: [ + "odoc" {< "1.3.0"} + "dune-release" {< "1.3.0"} + "jbuilder" {= "transition"} +] +dev-repo: "git+https://github.com/ocaml/dune.git" +build: [ + # opam 2 sets OPAM_SWITCH_PREFIX, so we don't need a hardcoded path + ["ocaml" "configure.ml" "--libdir" lib] {opam-version < "2"} + ["ocaml" "bootstrap.ml" "-j" jobs] + ["./dune.exe" "build" "-p" name "--profile" "dune-bootstrap" "-j" jobs] +] +url { + src: "https://github.com/ocaml/dune/releases/download/2.0.1/dune-2.0.1.tbz" + checksum: [ + "sha256=e04090c846f005f1cc02c390e963a7efe74c653ce2c5c7fd2e7e30a06ceadcb7" + "sha512=8c973ccfa1de0ff7173e17dac74ea850446a057866d47c7a100b271c7e440d5e607f1bfaa8fa5b756e0439492276e8c6615fac30cbff9ea900dc8e891f7ba4d3" + ] +} diff --git a/lib/esy.lock/opam/easy-format.1.3.2/opam b/lib/esy.lock/opam/easy-format.1.3.2/opam new file mode 100644 index 00000000..138d0fb2 --- /dev/null +++ b/lib/esy.lock/opam/easy-format.1.3.2/opam @@ -0,0 +1,46 @@ +opam-version: "2.0" +build: [ + ["dune" "subst"] {pinned} + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} + ["dune" "build" "-p" name "@doc"] {with-doc} +] +maintainer: ["martin@mjambon.com" "rudi.grinberg@gmail.com"] +authors: ["Martin Jambon"] +bug-reports: "https://github.com/mjambon/easy-format/issues" +homepage: "https://github.com/mjambon/easy-format" +doc: "https://mjambon.github.io/easy-format/" +license: "BSD-3-Clause" +dev-repo: "git+https://github.com/mjambon/easy-format.git" +synopsis: + "High-level and functional interface to the Format module of the OCaml standard library" +description: """ + +This module offers a high-level and functional interface to the Format module of +the OCaml standard library. It is a pretty-printing facility, i.e. it takes as +input some code represented as a tree and formats this code into the most +visually satisfying result, breaking and indenting lines of code where +appropriate. + +Input data must be first modelled and converted into a tree using 3 kinds of +nodes: + +* atoms +* lists +* labelled nodes + +Atoms represent any text that is guaranteed to be printed as-is. Lists can model +any sequence of items such as arrays of data or lists of definitions that are +labelled with something like "int main", "let x =" or "x:".""" +depends: [ + "dune" {>= "1.10"} + "ocaml" {>= "4.02.3"} +] +url { + src: + "https://github.com/mjambon/easy-format/releases/download/1.3.2/easy-format-1.3.2.tbz" + checksum: [ + "sha256=3440c2b882d537ae5e9011eb06abb53f5667e651ea4bb3b460ea8230fa8c1926" + "sha512=e39377a2ff020ceb9ac29e8515a89d9bdbc91dfcfa871c4e3baafa56753fac2896768e5d9822a050dc1e2ade43c8967afb69391a386c0a8ecd4e1f774e236135" + ] +} diff --git a/lib/esy.lock/opam/fmt.0.8.8/opam b/lib/esy.lock/opam/fmt.0.8.8/opam new file mode 100644 index 00000000..f493b5f5 --- /dev/null +++ b/lib/esy.lock/opam/fmt.0.8.8/opam @@ -0,0 +1,44 @@ +opam-version: "2.0" +maintainer: "Daniel Bünzli " +authors: [ "The fmt programmers" ] +homepage: "https://erratique.ch/software/fmt" +doc: "https://erratique.ch/software/fmt" +dev-repo: "git+https://erratique.ch/repos/fmt.git" +bug-reports: "https://github.com/dbuenzli/fmt/issues" +tags: [ "string" "format" "pretty-print" "org:erratique" ] +license: "ISC" +depends: [ + "ocaml" {>= "4.05.0"} + "ocamlfind" {build} + "ocamlbuild" {build} + "topkg" {build & >= "0.9.0"} + # Can be removed once ocaml >= 4.07 + "seq" + "stdlib-shims" +] +depopts: [ "base-unix" "cmdliner" ] +conflicts: [ "cmdliner" {< "0.9.8"} ] +build: [[ + "ocaml" "pkg/pkg.ml" "build" + "--dev-pkg" "%{pinned}%" + "--with-base-unix" "%{base-unix:installed}%" + "--with-cmdliner" "%{cmdliner:installed}%" ]] + +synopsis: """OCaml Format pretty-printer combinators""" +description: """\ + +Fmt exposes combinators to devise `Format` pretty-printing functions. + +Fmt depends only on the OCaml standard library. The optional `Fmt_tty` +library that allows to setup formatters for terminal color output +depends on the Unix library. The optional `Fmt_cli` library that +provides command line support for Fmt depends on [`Cmdliner`][cmdliner]. + +Fmt is distributed under the ISC license. + +[cmdliner]: http://erratique.ch/software/cmdliner +""" +url { +archive: "https://erratique.ch/software/fmt/releases/fmt-0.8.8.tbz" +checksum: "473490fcfdf3ff0a8ccee226b873d4b2" +} diff --git a/lib/esy.lock/opam/jbuilder.1.0+beta20.2/opam b/lib/esy.lock/opam/jbuilder.1.0+beta20.2/opam new file mode 100644 index 00000000..2e411c9e --- /dev/null +++ b/lib/esy.lock/opam/jbuilder.1.0+beta20.2/opam @@ -0,0 +1,39 @@ +opam-version: "2.0" +maintainer: "opensource@janestreet.com" +authors: ["Jane Street Group, LLC "] +homepage: "https://github.com/ocaml/dune" +bug-reports: "https://github.com/ocaml/dune/issues" +dev-repo: "git+https://github.com/ocaml/dune.git" +license: "Apache-2.0" +build: [ + ["ocaml" "configure.ml" "--libdir" lib] + ["ocaml" "bootstrap.ml"] + ["./boot.exe" "--subst"] {pinned} + ["./boot.exe" "-j" jobs] +] +synopsis: "Fast, portable and opinionated build system" +description: """ +jbuilder is a build system that was designed to simplify the release +of Jane Street packages. It reads metadata from "jbuild" files +following a very simple s-expression syntax. + +jbuilder is fast, it has very low-overhead and support parallel builds +on all platforms. It has no system dependencies, all you need to build +jbuilder and packages using jbuilder is OCaml. You don't need or make +or bash as long as the packages themselves don't use bash explicitely. + +jbuilder supports multi-package development by simply dropping multiple +repositories into the same directory. + +It also supports multi-context builds, such as building against +several opam roots/switches simultaneously. This helps maintaining +packages across several versions of OCaml and gives cross-compilation +for free.""" +depends: [ + "ocaml" {>= "4.02.3"} +] +url { + src: + "https://github.com/ocaml/dune/releases/download/1.0%2Bbeta20.2/jbuilder-1.0+beta20.2.tbz" + checksum: "md5=fbe8c3b1facb206cac3fb8932b5dd5d9" +} diff --git a/lib/esy.lock/opam/menhir.20200123/opam b/lib/esy.lock/opam/menhir.20200123/opam new file mode 100644 index 00000000..356699e4 --- /dev/null +++ b/lib/esy.lock/opam/menhir.20200123/opam @@ -0,0 +1,27 @@ +opam-version: "2.0" +maintainer: "francois.pottier@inria.fr" +authors: [ + "François Pottier " + "Yann Régis-Gianas " +] +homepage: "http://gitlab.inria.fr/fpottier/menhir" +dev-repo: "git+https://gitlab.inria.fr/fpottier/menhir.git" +bug-reports: "menhir@inria.fr" +build: [ + ["dune" "build" "-p" name "-j" jobs] +] +depends: [ + "ocaml" {>= "4.02.3"} + "dune" {>= "2.0.0"} + "menhirLib" {= version} + "menhirSdk" {= version} +] +synopsis: "An LR(1) parser generator" +url { + src: + "https://gitlab.inria.fr/fpottier/menhir/repository/20200123/archive.tar.gz" + checksum: [ + "md5=91aeae45fbf781e82ec3fe636be6ad49" + "sha512=4a7c4a72d4437940a0f62d402f783efcf357dde6f0a9e9f164c315148776e4642a822b6472f1e6e641164d110bc1ee05a6c1ad4a733f5defe4603b6072c1a34f" + ] +} diff --git a/lib/esy.lock/opam/menhirLib.20200211/opam b/lib/esy.lock/opam/menhirLib.20200211/opam new file mode 100644 index 00000000..28d3e426 --- /dev/null +++ b/lib/esy.lock/opam/menhirLib.20200211/opam @@ -0,0 +1,25 @@ +opam-version: "2.0" +maintainer: "francois.pottier@inria.fr" +authors: [ + "François Pottier " + "Yann Régis-Gianas " +] +homepage: "http://gitlab.inria.fr/fpottier/menhir" +dev-repo: "git+https://gitlab.inria.fr/fpottier/menhir.git" +bug-reports: "menhir@inria.fr" +build: [ + ["dune" "build" "-p" name "-j" jobs] +] +depends: [ + "ocaml" {>= "4.02.3"} + "dune" {>= "2.0.0"} +] +synopsis: "Runtime support library for parsers generated by Menhir" +url { + src: + "https://gitlab.inria.fr/fpottier/menhir/repository/20200211/archive.tar.gz" + checksum: [ + "md5=01577e5f15380c35bdaa8fd818204560" + "sha512=a686c4b047d5236c425afcd7f179964191268ff448b8d18510579d742a7256855049bc4fe568bb8f1b0d6cbfb758d95cd05e621e3410b75245bb799d623725d6" + ] +} diff --git a/lib/esy.lock/opam/menhirSdk.20200211/opam b/lib/esy.lock/opam/menhirSdk.20200211/opam new file mode 100644 index 00000000..524045ea --- /dev/null +++ b/lib/esy.lock/opam/menhirSdk.20200211/opam @@ -0,0 +1,25 @@ +opam-version: "2.0" +maintainer: "francois.pottier@inria.fr" +authors: [ + "François Pottier " + "Yann Régis-Gianas " +] +homepage: "http://gitlab.inria.fr/fpottier/menhir" +dev-repo: "git+https://gitlab.inria.fr/fpottier/menhir.git" +bug-reports: "menhir@inria.fr" +build: [ + ["dune" "build" "-p" name "-j" jobs] +] +depends: [ + "ocaml" {>= "4.02.3"} + "dune" {>= "2.0.0"} +] +synopsis: "Compile-time library for auxiliary tools related to Menhir" +url { + src: + "https://gitlab.inria.fr/fpottier/menhir/repository/20200211/archive.tar.gz" + checksum: [ + "md5=01577e5f15380c35bdaa8fd818204560" + "sha512=a686c4b047d5236c425afcd7f179964191268ff448b8d18510579d742a7256855049bc4fe568bb8f1b0d6cbfb758d95cd05e621e3410b75245bb799d623725d6" + ] +} diff --git a/lib/esy.lock/opam/merlin-extend.0.5/opam b/lib/esy.lock/opam/merlin-extend.0.5/opam new file mode 100644 index 00000000..a3ae0d30 --- /dev/null +++ b/lib/esy.lock/opam/merlin-extend.0.5/opam @@ -0,0 +1,29 @@ +opam-version: "2.0" +maintainer: "Frederic Bour " +authors: "Frederic Bour " +homepage: "https://github.com/let-def/merlin-extend" +bug-reports: "https://github.com/let-def/merlin-extend" +license: "MIT" +dev-repo: "git+https://github.com/let-def/merlin-extend.git" +build: [ + ["dune" "subst"] {pinned} + ["dune" "build" "-p" name "-j" jobs] +] +depends: [ + "dune" {>= "1.0"} + "cppo" {build} + "ocaml" {>= "4.02.3"} +] +synopsis: "A protocol to provide custom frontend to Merlin" +description: """ +This protocol allows to replace the OCaml frontend of Merlin. +It extends what used to be done with the `-pp' flag to handle a few more cases.""" +doc: "https://let-def.github.io/merlin-extend" +url { + src: + "https://github.com/let-def/merlin-extend/releases/download/v0.5/merlin-extend-v0.5.tbz" + checksum: [ + "sha256=ca3a38c360c7d4827eb4789abf7a6aa4b6e3b4e3c3ef69a5be64dce4601ec227" + "sha512=55c5a3637337abb8ca8db679128a81ca8ccce567bc214d55b2e6444dc0e905b74c64d629bdea2457d0fe4be5306414feefcdbc4d4761fdafd59aa107550936b6" + ] +} diff --git a/lib/esy.lock/opam/merlin.3.3.3/opam b/lib/esy.lock/opam/merlin.3.3.3/opam new file mode 100644 index 00000000..f0db8e9c --- /dev/null +++ b/lib/esy.lock/opam/merlin.3.3.3/opam @@ -0,0 +1,71 @@ +opam-version: "2.0" +maintainer: "defree@gmail.com" +authors: "The Merlin team" +homepage: "https://github.com/ocaml/merlin" +bug-reports: "https://github.com/ocaml/merlin/issues" +dev-repo: "git+https://github.com/ocaml/merlin.git" +build: [ + ["dune" "subst"] {pinned} + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} +] +depends: [ + "ocaml" {>= "4.02.1" & < "4.10"} + "dune" {>= "1.8.0"} + "ocamlfind" {>= "1.5.2"} + "yojson" {>= "1.6.0"} + "mdx" {with-test & >= "1.3.0"} + "conf-jq" {with-test} +] +synopsis: + "Editor helper, provides completion, typing and source browsing in Vim and Emacs" +description: + "Merlin is an assistant for editing OCaml code. It aims to provide the features available in modern IDEs: error reporting, auto completion, source browsing and much more." +post-messages: [ + "merlin installed. + +Quick setup for VIM +------------------- +Append this to your .vimrc to add merlin to vim's runtime-path: + let g:opamshare = substitute(system('opam config var share'),'\\n$','','''') + execute \"set rtp+=\" . g:opamshare . \"/merlin/vim\" + +Also run the following line in vim to index the documentation: + :execute \"helptags \" . g:opamshare . \"/merlin/vim/doc\" + +Quick setup for EMACS +------------------- +Add opam emacs directory to your load-path by appending this to your .emacs: + (let ((opam-share (ignore-errors (car (process-lines \"opam\" \"config\" \"var\" \"share\"))))) + (when (and opam-share (file-directory-p opam-share)) + ;; Register Merlin + (add-to-list 'load-path (expand-file-name \"emacs/site-lisp\" opam-share)) + (autoload 'merlin-mode \"merlin\" nil t nil) + ;; Automatically start it in OCaml buffers + (add-hook 'tuareg-mode-hook 'merlin-mode t) + (add-hook 'caml-mode-hook 'merlin-mode t) + ;; Use opam switch to lookup ocamlmerlin binary + (setq merlin-command 'opam))) + +Take a look at https://github.com/ocaml/merlin for more information + +Quick setup with opam-user-setup +-------------------------------- + +Opam-user-setup support Merlin. + + $ opam user-setup install + +should take care of basic setup. +See https://github.com/OCamlPro/opam-user-setup +" + {success & !user-setup:installed} +] +url { + src: + "https://github.com/ocaml/merlin/releases/download/v3.3.3/merlin-v3.3.3.tbz" + checksum: [ + "sha256=72909ef47eea1f6fca13b4109a34dccf8fe3923a3c026f1ed1db9eb5ee9aae15" + "sha512=2a5f39d966be56c1322982effc05bc98fd5f66cd12f1f76953f8daa9eca74a58c92a186854f4e601e2f0bb038720691446e7591b4613982accded3e579fedb23" + ] +} diff --git a/lib/esy.lock/opam/ocaml-compiler-libs.v0.12.1/opam b/lib/esy.lock/opam/ocaml-compiler-libs.v0.12.1/opam new file mode 100644 index 00000000..66f2549f --- /dev/null +++ b/lib/esy.lock/opam/ocaml-compiler-libs.v0.12.1/opam @@ -0,0 +1,23 @@ +opam-version: "2.0" +maintainer: "opensource@janestreet.com" +authors: ["Jane Street Group, LLC "] +homepage: "https://github.com/janestreet/ocaml-compiler-libs" +bug-reports: "https://github.com/janestreet/ocaml-compiler-libs/issues" +dev-repo: "git+https://github.com/janestreet/ocaml-compiler-libs.git" +license: "Apache-2.0" +build: [ + ["dune" "build" "-p" name "-j" jobs] +] +depends: [ + "ocaml" {>= "4.04.1"} + "dune" {>= "1.5.1"} +] +synopsis: "OCaml compiler libraries repackaged" +description: """ +This packages exposes the OCaml compiler libraries repackages under +the toplevel names Ocaml_common, Ocaml_bytecomp, Ocaml_optcomp, ...""" +url { + src: + "https://github.com/janestreet/ocaml-compiler-libs/archive/v0.12.1.tar.gz" + checksum: "md5=2f929af7c764a3f681a5671f271210c4" +} diff --git a/lib/esy.lock/opam/ocaml-migrate-parsetree.1.6.0/opam b/lib/esy.lock/opam/ocaml-migrate-parsetree.1.6.0/opam new file mode 100644 index 00000000..2437975a --- /dev/null +++ b/lib/esy.lock/opam/ocaml-migrate-parsetree.1.6.0/opam @@ -0,0 +1,37 @@ +opam-version: "2.0" +maintainer: "frederic.bour@lakaban.net" +authors: [ + "Frédéric Bour " + "Jérémie Dimino " +] +license: "LGPL-2.1 with OCaml linking exception" +homepage: "https://github.com/ocaml-ppx/ocaml-migrate-parsetree" +bug-reports: "https://github.com/ocaml-ppx/ocaml-migrate-parsetree/issues" +dev-repo: "git+https://github.com/ocaml-ppx/ocaml-migrate-parsetree.git" +doc: "https://ocaml-ppx.github.io/ocaml-migrate-parsetree/" +tags: [ "syntax" "org:ocamllabs" ] +build: [ + ["dune" "build" "-p" name "-j" jobs] +] +depends: [ + "result" + "ppx_derivers" + "dune" {>= "1.9.0"} + "ocaml" {>= "4.02.3"} +] +synopsis: "Convert OCaml parsetrees between different versions" +description: """ +Convert OCaml parsetrees between different versions + +This library converts parsetrees, outcometree and ast mappers between +different OCaml versions. High-level functions help making PPX +rewriters independent of a compiler version. +""" +url { + src: + "https://github.com/ocaml-ppx/ocaml-migrate-parsetree/releases/download/v1.6.0/ocaml-migrate-parsetree-v1.6.0.tbz" + checksum: [ + "sha256=9b018e7d25114ce17fc0b82b7cd7c927b84ebb6b043aa987fa7731c2484de33f" + "sha512=e03a5fe44ecf43683c764a7285a65bfa80639c09badf422661723bc3483d6d799c47c1ead34c2caa289a37e1b4b46d809c8cc56537d5c76e6004849d2d8a305f" + ] +} diff --git a/lib/esy.lock/opam/ocamlbuild.0.14.0/opam b/lib/esy.lock/opam/ocamlbuild.0.14.0/opam new file mode 100644 index 00000000..8deabeed --- /dev/null +++ b/lib/esy.lock/opam/ocamlbuild.0.14.0/opam @@ -0,0 +1,36 @@ +opam-version: "2.0" +maintainer: "Gabriel Scherer " +authors: ["Nicolas Pouillard" "Berke Durak"] +homepage: "https://github.com/ocaml/ocamlbuild/" +bug-reports: "https://github.com/ocaml/ocamlbuild/issues" +license: "LGPL-2.1-only with OCaml-LGPL-linking-exception" +doc: "https://github.com/ocaml/ocamlbuild/blob/master/manual/manual.adoc" +dev-repo: "git+https://github.com/ocaml/ocamlbuild.git" +build: [ + [ + make + "-f" + "configure.make" + "all" + "OCAMLBUILD_PREFIX=%{prefix}%" + "OCAMLBUILD_BINDIR=%{bin}%" + "OCAMLBUILD_LIBDIR=%{lib}%" + "OCAMLBUILD_MANDIR=%{man}%" + "OCAML_NATIVE=%{ocaml:native}%" + "OCAML_NATIVE_TOOLS=%{ocaml:native}%" + ] + [make "check-if-preinstalled" "all" "opam-install"] +] +conflicts: [ + "base-ocamlbuild" + "ocamlfind" {< "1.6.2"} +] +synopsis: + "OCamlbuild is a build system with builtin rules to easily build most OCaml projects." +depends: [ + "ocaml" {>= "4.03"} +] +url { + src: "https://github.com/ocaml/ocamlbuild/archive/0.14.0.tar.gz" + checksum: "sha256=87b29ce96958096c0a1a8eeafeb6268077b2d11e1bf2b3de0f5ebc9cf8d42e78" +} diff --git a/lib/esy.lock/opam/ocamlfind.1.8.1/files/ocaml-stub b/lib/esy.lock/opam/ocamlfind.1.8.1/files/ocaml-stub new file mode 100644 index 00000000..e5ad9907 --- /dev/null +++ b/lib/esy.lock/opam/ocamlfind.1.8.1/files/ocaml-stub @@ -0,0 +1,4 @@ +#!/bin/sh + +BINDIR=$(dirname "$(command -v ocamlc)") +"$BINDIR/ocaml" -I "$OCAML_TOPLEVEL_PATH" "$@" diff --git a/lib/esy.lock/opam/ocamlfind.1.8.1/files/ocamlfind.install b/lib/esy.lock/opam/ocamlfind.1.8.1/files/ocamlfind.install new file mode 100644 index 00000000..295c6254 --- /dev/null +++ b/lib/esy.lock/opam/ocamlfind.1.8.1/files/ocamlfind.install @@ -0,0 +1,6 @@ +bin: [ + "src/findlib/ocamlfind" {"ocamlfind"} + "?src/findlib/ocamlfind_opt" {"ocamlfind"} + "?tools/safe_camlp4" +] +toplevel: ["src/findlib/topfind"] diff --git a/lib/esy.lock/opam/ocamlfind.1.8.1/opam b/lib/esy.lock/opam/ocamlfind.1.8.1/opam new file mode 100644 index 00000000..d757d669 --- /dev/null +++ b/lib/esy.lock/opam/ocamlfind.1.8.1/opam @@ -0,0 +1,50 @@ +opam-version: "2.0" +synopsis: "A library manager for OCaml" +maintainer: "Thomas Gazagnaire " +authors: "Gerd Stolpmann " +homepage: "http://projects.camlcity.org/projects/findlib.html" +bug-reports: "https://gitlab.camlcity.org/gerd/lib-findlib/issues" +dev-repo: "git+https://gitlab.camlcity.org/gerd/lib-findlib.git" +description: """ +Findlib is a library manager for OCaml. It provides a convention how +to store libraries, and a file format ("META") to describe the +properties of libraries. There is also a tool (ocamlfind) for +interpreting the META files, so that it is very easy to use libraries +in programs and scripts. +""" +build: [ + [ + "./configure" + "-bindir" + bin + "-sitelib" + lib + "-mandir" + man + "-config" + "%{lib}%/findlib.conf" + "-no-custom" + "-no-camlp4" {!ocaml:preinstalled & ocaml:version >= "4.02.0"} + "-no-topfind" {ocaml:preinstalled} + ] + [make "all"] + [make "opt"] {ocaml:native} +] +install: [ + [make "install"] + ["install" "-m" "0755" "ocaml-stub" "%{bin}%/ocaml"] {ocaml:preinstalled} +] +depends: [ + "ocaml" {>= "4.00.0"} + "conf-m4" {build} +] +extra-files: [ + ["ocamlfind.install" "md5=06f2c282ab52d93aa6adeeadd82a2543"] + ["ocaml-stub" "md5=181f259c9e0bad9ef523e7d4abfdf87a"] +] +url { + src: "http://download.camlcity.org/download/findlib-1.8.1.tar.gz" + checksum: "md5=18ca650982c15536616dea0e422cbd8c" + mirrors: "http://download2.camlcity.org/download/findlib-1.8.1.tar.gz" +} +depopts: ["graphics"] diff --git a/lib/esy.lock/opam/ppx_derivers.1.2.1/opam b/lib/esy.lock/opam/ppx_derivers.1.2.1/opam new file mode 100644 index 00000000..3d10814e --- /dev/null +++ b/lib/esy.lock/opam/ppx_derivers.1.2.1/opam @@ -0,0 +1,23 @@ +opam-version: "2.0" +maintainer: "jeremie@dimino.org" +authors: ["Jérémie Dimino"] +license: "BSD-3-Clause" +homepage: "https://github.com/ocaml-ppx/ppx_derivers" +bug-reports: "https://github.com/ocaml-ppx/ppx_derivers/issues" +dev-repo: "git://github.com/ocaml-ppx/ppx_derivers.git" +build: [ + ["dune" "build" "-p" name "-j" jobs] +] +depends: [ + "ocaml" + "dune" +] +synopsis: "Shared [@@deriving] plugin registry" +description: """ +Ppx_derivers is a tiny package whose sole purpose is to allow +ppx_deriving and ppx_type_conv to inter-operate gracefully when linked +as part of the same ocaml-migrate-parsetree driver.""" +url { + src: "https://github.com/ocaml-ppx/ppx_derivers/archive/1.2.1.tar.gz" + checksum: "md5=5dc2bf130c1db3c731fe0fffc5648b41" +} diff --git a/lib/esy.lock/opam/ppxlib.0.12.0/opam b/lib/esy.lock/opam/ppxlib.0.12.0/opam new file mode 100644 index 00000000..6a41efce --- /dev/null +++ b/lib/esy.lock/opam/ppxlib.0.12.0/opam @@ -0,0 +1,46 @@ +opam-version: "2.0" +maintainer: "opensource@janestreet.com" +authors: ["Jane Street Group, LLC "] +homepage: "https://github.com/ocaml-ppx/ppxlib" +bug-reports: "https://github.com/ocaml-ppx/ppxlib/issues" +dev-repo: "git+https://github.com/ocaml-ppx/ppxlib.git" +doc: "https://ocaml-ppx.github.io/ppxlib/" +license: "MIT" +build: [ + ["dune" "subst"] {pinned} + ["dune" "build" "-p" name "-j" jobs] +] +run-test: [ + ["dune" "runtest" "-p" name "-j" jobs] { ocaml:version >= "4.06" & ocaml:version < "4.08" } +] +depends: [ + "ocaml" {>= "4.04.1" & < "4.11.0"} + "base" {>= "v0.11.0"} + "dune" {>= "1.11"} + "ocaml-compiler-libs" {>= "v0.11.0"} + "ocaml-migrate-parsetree" {>= "1.3.1"} + "ppx_derivers" {>= "1.0"} + "stdio" {>= "v0.11.0"} + "ocamlfind" {with-test} + "cinaps" {with-test & >= "v0.12.1"} +] +synopsis: "Base library and tools for ppx rewriters" +description: """ +A comprehensive toolbox for ppx development. It features: +- a OCaml AST / parser / pretty-printer snapshot,to create a full + frontend independent of the version of OCaml; +- a library for library for ppx rewriters in general, and type-driven + code generators in particular; +- a feature-full driver for OCaml AST transformers; +- a quotation mechanism allowing to write values representing the + OCaml AST in the OCaml syntax; +- a generator of open recursion classes from type definitions. +""" +url { + src: + "https://github.com/ocaml-ppx/ppxlib/archive/0.12.0.tar.gz" + checksum: [ + "sha256=6b562c9b3b9350777318729921f890850b385c469db60769aafd9371998a2c42" + "sha512=2372a7a53d857389978e617c95183289547d53caa5e83a7d867cab347b114b719667bd09eaf2e2334085ef92691a99b42871f6410ffb2977b0b8724014c80a70" + ] +} diff --git a/lib/esy.lock/opam/re.1.9.0/opam b/lib/esy.lock/opam/re.1.9.0/opam new file mode 100644 index 00000000..f7987544 --- /dev/null +++ b/lib/esy.lock/opam/re.1.9.0/opam @@ -0,0 +1,42 @@ +opam-version: "2.0" + +maintainer: "rudi.grinberg@gmail.com" +authors: [ + "Jerome Vouillon" + "Thomas Gazagnaire" + "Anil Madhavapeddy" + "Rudi Grinberg" + "Gabriel Radanne" +] +license: "LGPL-2.0-only with OCaml-LGPL-linking-exception" +homepage: "https://github.com/ocaml/ocaml-re" +bug-reports: "https://github.com/ocaml/ocaml-re/issues" +dev-repo: "git+https://github.com/ocaml/ocaml-re.git" + +build: [ + ["dune" "subst"] {pinned} + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} +] + +depends: [ + "ocaml" {>= "4.02"} + "dune" + "ounit" {with-test} + "seq" +] + +synopsis: "RE is a regular expression library for OCaml" +description: """ +Pure OCaml regular expressions with: +* Perl-style regular expressions (module Re.Perl) +* Posix extended regular expressions (module Re.Posix) +* Emacs-style regular expressions (module Re.Emacs) +* Shell-style file globbing (module Re.Glob) +* Compatibility layer for OCaml's built-in Str module (module Re.Str) +""" +url { + src: + "https://github.com/ocaml/ocaml-re/releases/download/1.9.0/re-1.9.0.tbz" + checksum: "md5=bddaed4f386a22cace7850c9c7dac296" +} diff --git a/lib/esy.lock/opam/result.1.5/opam b/lib/esy.lock/opam/result.1.5/opam new file mode 100644 index 00000000..671af042 --- /dev/null +++ b/lib/esy.lock/opam/result.1.5/opam @@ -0,0 +1,22 @@ +opam-version: "2.0" +maintainer: "opensource@janestreet.com" +authors: ["Jane Street Group, LLC "] +homepage: "https://github.com/janestreet/result" +dev-repo: "git+https://github.com/janestreet/result.git" +bug-reports: "https://github.com/janestreet/result/issues" +license: "BSD-3-Clause" +build: [["dune" "build" "-p" name "-j" jobs]] +depends: [ + "ocaml" + "dune" {>= "1.0"} +] +synopsis: "Compatibility Result module" +description: """ +Projects that want to use the new result type defined in OCaml >= 4.03 +while staying compatible with older version of OCaml should use the +Result module defined in this library.""" +url { + src: + "https://github.com/janestreet/result/releases/download/1.5/result-1.5.tbz" + checksum: "md5=1b82dec78849680b49ae9a8a365b831b" +} diff --git a/lib/esy.lock/opam/seq.0.2.2/opam b/lib/esy.lock/opam/seq.0.2.2/opam new file mode 100644 index 00000000..5ed51654 --- /dev/null +++ b/lib/esy.lock/opam/seq.0.2.2/opam @@ -0,0 +1,24 @@ +opam-version: "2.0" +synopsis: + "Compatibility package for OCaml's standard iterator type starting from 4.07" +maintainer: "simon.cruanes.2007@m4x.org" +license: "LGPL2.1" +build: [ + ["dune" "build" "-p" name "-j" jobs] +] +depends: [ + "dune" {>= "1.1.0"} + "ocaml" +] +tags: [ "iterator" "seq" "pure" "list" "compatibility" "cascade" ] +homepage: "https://github.com/c-cube/seq/" +bug-reports: "https://github.com/c-cube/seq/issues" +dev-repo: "git+https://github.com/c-cube/seq.git" +authors: "Simon Cruanes" +url { + src: "https://github.com/c-cube/seq/archive/0.2.2.tar.gz" + checksum: [ + "md5=9033e02283aa3bde9f97f24e632902e3" + "sha512=cab0eb4cb6d9788b7cbd7acbefefc15689d706c97ff7f75dd97faf3c21e466af4d0ff110541a24729db587e7172b1a30a3c2967e17ec2e49cbd923360052c07c" + ] +} diff --git a/lib/esy.lock/opam/sexplib0.v0.13.0/opam b/lib/esy.lock/opam/sexplib0.v0.13.0/opam new file mode 100644 index 00000000..27626b3c --- /dev/null +++ b/lib/esy.lock/opam/sexplib0.v0.13.0/opam @@ -0,0 +1,26 @@ +opam-version: "2.0" +maintainer: "opensource@janestreet.com" +authors: ["Jane Street Group, LLC "] +homepage: "https://github.com/janestreet/sexplib0" +bug-reports: "https://github.com/janestreet/sexplib0/issues" +dev-repo: "git+https://github.com/janestreet/sexplib0.git" +doc: "https://ocaml.janestreet.com/ocaml-core/latest/doc/sexplib0/index.html" +license: "MIT" +build: [ + ["dune" "build" "-p" name "-j" jobs] +] +depends: [ + "ocaml" {>= "4.04.2"} + "dune" {>= "1.5.1"} +] +synopsis: "Library containing the definition of S-expressions and some base converters" +description: " +Part of Jane Street's Core library +The Core suite of libraries is an industrial strength alternative to +OCaml's standard library that was developed by Jane Street, the +largest industrial user of OCaml. +" +url { + src: "https://ocaml.janestreet.com/ocaml-core/v0.13/files/sexplib0-v0.13.0.tar.gz" + checksum: "md5=f8a715dffda5599cfae0cb4031d57abe" +} diff --git a/lib/esy.lock/opam/stdio.v0.13.0/opam b/lib/esy.lock/opam/stdio.v0.13.0/opam new file mode 100644 index 00000000..42d6f14d --- /dev/null +++ b/lib/esy.lock/opam/stdio.v0.13.0/opam @@ -0,0 +1,27 @@ +opam-version: "2.0" +maintainer: "opensource@janestreet.com" +authors: ["Jane Street Group, LLC "] +homepage: "https://github.com/janestreet/stdio" +bug-reports: "https://github.com/janestreet/stdio/issues" +dev-repo: "git+https://github.com/janestreet/stdio.git" +doc: "https://ocaml.janestreet.com/ocaml-core/latest/doc/stdio/index.html" +license: "MIT" +build: [ + ["dune" "build" "-p" name "-j" jobs] +] +depends: [ + "ocaml" {>= "4.04.2"} + "base" {>= "v0.13" & < "v0.14"} + "dune" {>= "1.5.1"} +] +synopsis: "Standard IO library for OCaml" +description: " +Stdio implements simple input/output functionalities for OCaml. + +It re-exports the input/output functions of the OCaml standard +libraries using a more consistent API. +" +url { + src: "https://ocaml.janestreet.com/ocaml-core/v0.13/files/stdio-v0.13.0.tar.gz" + checksum: "md5=48ef28512ddd51ff9885649dd1fab91d" +} diff --git a/lib/esy.lock/opam/stdlib-shims.0.1.0/opam b/lib/esy.lock/opam/stdlib-shims.0.1.0/opam new file mode 100644 index 00000000..5839c43c --- /dev/null +++ b/lib/esy.lock/opam/stdlib-shims.0.1.0/opam @@ -0,0 +1,27 @@ +opam-version: "2.0" +maintainer: "The stdlib-shims programmers" +authors: "The stdlib-shims programmers" +homepage: "https://github.com/ocaml/stdlib-shims" +doc: "https://ocaml.github.io/stdlib-shims/" +dev-repo: "git+https://github.com/ocaml/stdlib-shims.git" +bug-reports: "https://github.com/ocaml/stdlib-shims/issues" +tags: ["stdlib" "compatibility" "org:ocaml"] +license: ["typeof OCaml system"] +depends: [ + "dune" + "ocaml" {>= "4.02.3"} +] +build: [ "dune" "build" "-p" name "-j" jobs ] +synopsis: "Backport some of the new stdlib features to older compiler" +description: """ +Backport some of the new stdlib features to older compiler, +such as the Stdlib module. + +This allows projects that require compatibility with older compiler to +use these new features in their code. +""" +url { + src: + "https://github.com/ocaml/stdlib-shims/releases/download/0.1.0/stdlib-shims-0.1.0.tbz" + checksum: "md5=12b5704eed70c6bff5ac39a16db1425d" +} diff --git a/lib/esy.lock/opam/topkg.1.0.1/opam b/lib/esy.lock/opam/topkg.1.0.1/opam new file mode 100644 index 00000000..77ae1f42 --- /dev/null +++ b/lib/esy.lock/opam/topkg.1.0.1/opam @@ -0,0 +1,48 @@ +opam-version: "2.0" +maintainer: "Daniel Bünzli " +authors: ["Daniel Bünzli "] +homepage: "http://erratique.ch/software/topkg" +doc: "http://erratique.ch/software/topkg/doc" +license: "ISC" +dev-repo: "git+http://erratique.ch/repos/topkg.git" +bug-reports: "https://github.com/dbuenzli/topkg/issues" +tags: ["packaging" "ocamlbuild" "org:erratique"] +depends: [ + "ocaml" {>= "4.03.0"} + "ocamlfind" {build & >= "1.6.1"} + "ocamlbuild" ] +build: [[ + "ocaml" "pkg/pkg.ml" "build" + "--pkg-name" name + "--dev-pkg" "%{pinned}%" ]] +synopsis: """The transitory OCaml software packager""" +description: """\ + +Topkg is a packager for distributing OCaml software. It provides an +API to describe the files a package installs in a given build +configuration and to specify information about the package's +distribution, creation and publication procedures. + +The optional topkg-care package provides the `topkg` command line tool +which helps with various aspects of a package's life cycle: creating +and linting a distribution, releasing it on the WWW, publish its +documentation, add it to the OCaml opam repository, etc. + +Topkg is distributed under the ISC license and has **no** +dependencies. This is what your packages will need as a *build* +dependency. + +Topkg-care is distributed under the ISC license it depends on +[fmt][fmt], [logs][logs], [bos][bos], [cmdliner][cmdliner], +[webbrowser][webbrowser] and `opam-format`. + +[fmt]: http://erratique.ch/software/fmt +[logs]: http://erratique.ch/software/logs +[bos]: http://erratique.ch/software/bos +[cmdliner]: http://erratique.ch/software/cmdliner +[webbrowser]: http://erratique.ch/software/webbrowser +""" +url { +archive: "http://erratique.ch/software/topkg/releases/topkg-1.0.1.tbz" +checksum: "16b90e066d8972a5ef59655e7c28b3e9" +} diff --git a/lib/esy.lock/opam/uuidm.0.9.7/opam b/lib/esy.lock/opam/uuidm.0.9.7/opam new file mode 100644 index 00000000..0c59ad93 --- /dev/null +++ b/lib/esy.lock/opam/uuidm.0.9.7/opam @@ -0,0 +1,34 @@ +opam-version: "2.0" +maintainer: "Daniel Bünzli " +authors: ["Daniel Bünzli "] +homepage: "https://erratique.ch/software/uuidm" +doc: "https://erratique.ch/software/uuidm/doc/Uuidm" +dev-repo: "git+https://erratique.ch/repos/uuidm.git" +bug-reports: "https://github.com/dbuenzli/uuidm/issues" +tags: [ "uuid" "codec" "org:erratique" ] +license: "ISC" +depends: [ + "ocaml" {>= "4.03.0"} + "ocamlfind" {build} + "ocamlbuild" {build} + "topkg" {build} ] +depopts: [ "cmdliner" ] +build: +[ "ocaml" "pkg/pkg.ml" "build" + "--pinned" "%{pinned}%" + "--with-cmdliner" "%{cmdliner:installed}%" ] +synopsis: """Universally unique identifiers (UUIDs) for OCaml""" +description: """\ + +Uuidm is an OCaml module implementing 128 bits universally unique +identifiers version 3, 5 (named based with MD5, SHA-1 hashing) and 4 +(random based) according to [RFC 4122][rfc4122]. + +Uuidm has no dependency and is distributed under the ISC license. + +[rfc4122]: http://tools.ietf.org/html/rfc4122 +""" +url { +archive: "https://erratique.ch/software/uuidm/releases/uuidm-0.9.7.tbz" +checksum: "54658248e3981d8c05237d0a4277ccd3" +} diff --git a/lib/esy.lock/opam/yojson.1.7.0/opam b/lib/esy.lock/opam/yojson.1.7.0/opam new file mode 100644 index 00000000..ffef0682 --- /dev/null +++ b/lib/esy.lock/opam/yojson.1.7.0/opam @@ -0,0 +1,38 @@ +opam-version: "2.0" +maintainer: "martin@mjambon.com" +authors: ["Martin Jambon"] +homepage: "https://github.com/ocaml-community/yojson" +bug-reports: "https://github.com/ocaml-community/yojson/issues" +dev-repo: "git+https://github.com/ocaml-community/yojson.git" +doc: "https://ocaml-community.github.io/yojson/" +build: [ + ["dune" "subst"] {pinned} + ["dune" "build" "-p" name "-j" jobs] +] +run-test: [["dune" "runtest" "-p" name "-j" jobs]] +depends: [ + "ocaml" {>= "4.02.3"} + "dune" + "cppo" {build} + "easy-format" + "biniou" {>= "1.2.0"} + "alcotest" {with-test & >= "0.8.5"} +] +synopsis: + "Yojson is an optimized parsing and printing library for the JSON format" +description: """ +Yojson is an optimized parsing and printing library for the JSON format. + +It addresses a few shortcomings of json-wheel including 2x speedup, +polymorphic variants and optional syntax for tuples and variants. + +ydump is a pretty-printing command-line program provided with the +yojson package. + +The program atdgen can be used to derive OCaml-JSON serializers and +deserializers from type definitions.""" +url { + src: + "https://github.com/ocaml-community/yojson/releases/download/1.7.0/yojson-1.7.0.tbz" + checksum: "md5=b89d39ca3f8c532abe5f547ad3b8f84d" +} diff --git a/lib/esy.lock/overrides/opam__s__ocamlbuild_opam__c__0.14.0_opam_override/files/ocamlbuild-0.14.0.patch b/lib/esy.lock/overrides/opam__s__ocamlbuild_opam__c__0.14.0_opam_override/files/ocamlbuild-0.14.0.patch new file mode 100644 index 00000000..4d5bea0e --- /dev/null +++ b/lib/esy.lock/overrides/opam__s__ocamlbuild_opam__c__0.14.0_opam_override/files/ocamlbuild-0.14.0.patch @@ -0,0 +1,463 @@ +--- ./Makefile ++++ ./Makefile +@@ -213,7 +213,7 @@ + rm -f man/ocamlbuild.1 + + man/options_man.byte: src/ocamlbuild_pack.cmo +- $(OCAMLC) $^ -I src man/options_man.ml -o man/options_man.byte ++ $(OCAMLC) -I +unix unix.cma $^ -I src man/options_man.ml -o man/options_man.byte + + clean:: + rm -f man/options_man.cm* +--- ./src/command.ml ++++ ./src/command.ml +@@ -148,9 +148,10 @@ + let self = string_of_command_spec_with_calls call_with_tags call_with_target resolve_virtuals in + let b = Buffer.create 256 in + (* The best way to prevent bash from switching to its windows-style +- * quote-handling is to prepend an empty string before the command name. *) ++ * quote-handling is to prepend an empty string before the command name. ++ * space seems to work, too - and the ouput is nicer *) + if Sys.os_type = "Win32" then +- Buffer.add_string b "''"; ++ Buffer.add_char b ' '; + let first = ref true in + let put_space () = + if !first then +@@ -260,7 +261,7 @@ + + let execute_many ?(quiet=false) ?(pretend=false) cmds = + add_parallel_stat (List.length cmds); +- let degraded = !*My_unix.is_degraded || Sys.os_type = "Win32" in ++ let degraded = !*My_unix.is_degraded in + let jobs = !jobs in + if jobs < 0 then invalid_arg "jobs < 0"; + let max_jobs = if jobs = 0 then None else Some jobs in +--- ./src/findlib.ml ++++ ./src/findlib.ml +@@ -66,9 +66,6 @@ + (fun command -> lexer & Lexing.from_string & run_and_read command) + command + +-let run_and_read command = +- Printf.ksprintf run_and_read command +- + let rec query name = + try + Hashtbl.find packages name +@@ -135,7 +132,8 @@ + with Not_found -> s + + let list () = +- List.map before_space (split_nl & run_and_read "%s list" ocamlfind) ++ let cmd = Shell.quote_filename_if_needed ocamlfind ^ " list" in ++ List.map before_space (split_nl & run_and_read cmd) + + (* The closure algorithm is easy because the dependencies are already closed + and sorted for each package. We only have to make the union. We could also +--- ./src/main.ml ++++ ./src/main.ml +@@ -162,6 +162,9 @@ + Tags.mem "traverse" tags + || List.exists (Pathname.is_prefix path_name) !Options.include_dirs + || List.exists (Pathname.is_prefix path_name) target_dirs) ++ && ((* beware: !Options.build_dir is an absolute directory *) ++ Pathname.normalize !Options.build_dir ++ <> Pathname.normalize (Pathname.pwd/path_name)) + end + end + end +--- ./src/my_std.ml ++++ ./src/my_std.ml +@@ -271,13 +271,107 @@ + try Array.iter (fun x -> if x = basename then raise Exit) a; false + with Exit -> true + ++let command_plain = function ++| [| |] -> 0 ++| margv -> ++ let rec waitpid a b = ++ match Unix.waitpid a b with ++ | exception (Unix.Unix_error(Unix.EINTR,_,_)) -> waitpid a b ++ | x -> x ++ in ++ let pid = Unix.(create_process margv.(0) margv stdin stdout stderr) in ++ let pid', process_status = waitpid [] pid in ++ assert (pid = pid'); ++ match process_status with ++ | Unix.WEXITED n -> n ++ | Unix.WSIGNALED _ -> 2 (* like OCaml's uncaught exceptions *) ++ | Unix.WSTOPPED _ -> 127 ++ ++(* can't use Lexers because of circular dependency *) ++let split_path_win str = ++ let rec aux pos = ++ try ++ let i = String.index_from str pos ';' in ++ let len = i - pos in ++ if len = 0 then ++ aux (succ i) ++ else ++ String.sub str pos (i - pos) :: aux (succ i) ++ with Not_found | Invalid_argument _ -> ++ let len = String.length str - pos in ++ if len = 0 then [] else [String.sub str pos len] ++ in ++ aux 0 ++ ++let windows_shell = lazy begin ++ let rec iter = function ++ | [] -> [| "bash.exe" ; "--norc" ; "--noprofile" |] ++ | hd::tl -> ++ let dash = Filename.concat hd "dash.exe" in ++ if Sys.file_exists dash then [|dash|] else ++ let bash = Filename.concat hd "bash.exe" in ++ if Sys.file_exists bash = false then iter tl else ++ (* if sh.exe and bash.exe exist in the same dir, choose sh.exe *) ++ let sh = Filename.concat hd "sh.exe" in ++ if Sys.file_exists sh then [|sh|] else [|bash ; "--norc" ; "--noprofile"|] ++ in ++ split_path_win (try Sys.getenv "PATH" with Not_found -> "") |> iter ++end ++ ++let prep_windows_cmd cmd = ++ (* workaround known ocaml bug, remove later *) ++ if String.contains cmd '\t' && String.contains cmd ' ' = false then ++ " " ^ cmd ++ else ++ cmd ++ ++let run_with_shell = function ++| "" -> 0 ++| cmd -> ++ let cmd = prep_windows_cmd cmd in ++ let shell = Lazy.force windows_shell in ++ let qlen = Filename.quote cmd |> String.length in ++ (* old versions of dash had problems with bs *) ++ try ++ if qlen < 7_900 then ++ command_plain (Array.append shell [| "-ec" ; cmd |]) ++ else begin ++ (* it can still work, if the called command is a cygwin tool *) ++ let ch_closed = ref false in ++ let file_deleted = ref false in ++ let fln,ch = ++ Filename.open_temp_file ++ ~mode:[Open_binary] ++ "ocamlbuildtmp" ++ ".sh" ++ in ++ try ++ let f_slash = String.map ( fun x -> if x = '\\' then '/' else x ) fln in ++ output_string ch cmd; ++ ch_closed:= true; ++ close_out ch; ++ let ret = command_plain (Array.append shell [| "-e" ; f_slash |]) in ++ file_deleted:= true; ++ Sys.remove fln; ++ ret ++ with ++ | x -> ++ if !ch_closed = false then ++ close_out_noerr ch; ++ if !file_deleted = false then ++ (try Sys.remove fln with _ -> ()); ++ raise x ++ end ++ with ++ | (Unix.Unix_error _) as x -> ++ (* Sys.command doesn't raise an exception, so run_with_shell also won't ++ raise *) ++ Printexc.to_string x ^ ":" ^ cmd |> prerr_endline; ++ 1 ++ + let sys_command = +- match Sys.os_type with +- | "Win32" -> fun cmd -> +- if cmd = "" then 0 else +- let cmd = "bash --norc -c " ^ Filename.quote cmd in +- Sys.command cmd +- | _ -> fun cmd -> if cmd = "" then 0 else Sys.command cmd ++ if Sys.win32 then run_with_shell ++ else fun cmd -> if cmd = "" then 0 else Sys.command cmd + + (* FIXME warning fix and use Filename.concat *) + let filename_concat x y = +--- ./src/my_std.mli ++++ ./src/my_std.mli +@@ -69,3 +69,6 @@ + + val split_ocaml_version : (int * int * int * string) option + (** (major, minor, patchlevel, rest) *) ++ ++val windows_shell : string array Lazy.t ++val prep_windows_cmd : string -> string +--- ./src/ocamlbuild_executor.ml ++++ ./src/ocamlbuild_executor.ml +@@ -34,6 +34,8 @@ + job_stdin : out_channel; + job_stderr : in_channel; + job_buffer : Buffer.t; ++ job_pid : int; ++ job_tmp_file: string option; + mutable job_dying : bool; + };; + +@@ -76,6 +78,61 @@ + in + loop 0 + ;; ++ ++let open_process_full_win cmd env = ++ let (in_read, in_write) = Unix.pipe () in ++ let (out_read, out_write) = Unix.pipe () in ++ let (err_read, err_write) = Unix.pipe () in ++ Unix.set_close_on_exec in_read; ++ Unix.set_close_on_exec out_write; ++ Unix.set_close_on_exec err_read; ++ let inchan = Unix.in_channel_of_descr in_read in ++ let outchan = Unix.out_channel_of_descr out_write in ++ let errchan = Unix.in_channel_of_descr err_read in ++ let shell = Lazy.force Ocamlbuild_pack.My_std.windows_shell in ++ let test_cmd = ++ String.concat " " (List.map Filename.quote (Array.to_list shell)) ^ ++ "-ec " ^ ++ Filename.quote (Ocamlbuild_pack.My_std.prep_windows_cmd cmd) in ++ let argv,tmp_file = ++ if String.length test_cmd < 7_900 then ++ Array.append ++ shell ++ [| "-ec" ; Ocamlbuild_pack.My_std.prep_windows_cmd cmd |],None ++ else ++ let fln,ch = Filename.open_temp_file ~mode:[Open_binary] "ocamlbuild" ".sh" in ++ output_string ch (Ocamlbuild_pack.My_std.prep_windows_cmd cmd); ++ close_out ch; ++ let fln' = String.map (function '\\' -> '/' | c -> c) fln in ++ Array.append ++ shell ++ [| "-c" ; fln' |], Some fln in ++ let pid = ++ Unix.create_process_env argv.(0) argv env out_read in_write err_write in ++ Unix.close out_read; ++ Unix.close in_write; ++ Unix.close err_write; ++ (pid, inchan, outchan, errchan,tmp_file) ++ ++let close_process_full_win (pid,inchan, outchan, errchan, tmp_file) = ++ let delete tmp_file = ++ match tmp_file with ++ | None -> () ++ | Some x -> try Sys.remove x with Sys_error _ -> () in ++ let tmp_file_deleted = ref false in ++ try ++ close_in inchan; ++ close_out outchan; ++ close_in errchan; ++ let res = snd(Unix.waitpid [] pid) in ++ tmp_file_deleted := true; ++ delete tmp_file; ++ res ++ with ++ | x when tmp_file <> None && !tmp_file_deleted = false -> ++ delete tmp_file; ++ raise x ++ + (* ***) + (*** execute *) + (* XXX: Add test for non reentrancy *) +@@ -130,10 +187,16 @@ + (*** add_job *) + let add_job cmd rest result id = + (*display begin fun oc -> fp oc "Job %a is %s\n%!" print_job_id id cmd; end;*) +- let (stdout', stdin', stderr') = open_process_full cmd env in ++ let (pid,stdout', stdin', stderr', tmp_file) = ++ if Sys.win32 then open_process_full_win cmd env else ++ let a,b,c = open_process_full cmd env in ++ -1,a,b,c,None ++ in + incr jobs_active; +- set_nonblock (doi stdout'); +- set_nonblock (doi stderr'); ++ if not Sys.win32 then ( ++ set_nonblock (doi stdout'); ++ set_nonblock (doi stderr'); ++ ); + let job = + { job_id = id; + job_command = cmd; +@@ -143,7 +206,9 @@ + job_stdin = stdin'; + job_stderr = stderr'; + job_buffer = Buffer.create 1024; +- job_dying = false } ++ job_dying = false; ++ job_tmp_file = tmp_file; ++ job_pid = pid } + in + outputs := FDM.add (doi stdout') job (FDM.add (doi stderr') job !outputs); + jobs := JS.add job !jobs; +@@ -199,6 +264,7 @@ + try + read fd u 0 (Bytes.length u) + with ++ | Unix.Unix_error(Unix.EPIPE,_,_) when Sys.win32 -> 0 + | Unix.Unix_error(e,_,_) -> + let msg = error_message e in + display (fun oc -> fp oc +@@ -241,14 +307,19 @@ + decr jobs_active; + + (* PR#5371: we would get EAGAIN below otherwise *) +- clear_nonblock (doi job.job_stdout); +- clear_nonblock (doi job.job_stderr); +- ++ if not Sys.win32 then ( ++ clear_nonblock (doi job.job_stdout); ++ clear_nonblock (doi job.job_stderr); ++ ); + do_read ~loop:true (doi job.job_stdout) job; + do_read ~loop:true (doi job.job_stderr) job; + outputs := FDM.remove (doi job.job_stdout) (FDM.remove (doi job.job_stderr) !outputs); + jobs := JS.remove job !jobs; +- let status = close_process_full (job.job_stdout, job.job_stdin, job.job_stderr) in ++ let status = ++ if Sys.win32 then ++ close_process_full_win (job.job_pid, job.job_stdout, job.job_stdin, job.job_stderr, job.job_tmp_file) ++ else ++ close_process_full (job.job_stdout, job.job_stdin, job.job_stderr) in + + let shown = ref false in + +--- ./src/ocamlbuild_unix_plugin.ml ++++ ./src/ocamlbuild_unix_plugin.ml +@@ -48,12 +48,22 @@ + end + + let run_and_open s kont = ++ let s_orig = s in ++ let s = ++ (* Be consistent! My_unix.run_and_open uses My_std.sys_command and ++ sys_command uses bash. *) ++ if Sys.win32 = false then s else ++ let l = match Lazy.force My_std.windows_shell |> Array.to_list with ++ | hd::tl -> (Filename.quote hd)::tl ++ | _ -> assert false in ++ "\"" ^ (String.concat " " l) ^ " -ec " ^ Filename.quote (" " ^ s) ^ "\"" ++ in + let ic = Unix.open_process_in s in + let close () = + match Unix.close_process_in ic with + | Unix.WEXITED 0 -> () + | Unix.WEXITED _ | Unix.WSIGNALED _ | Unix.WSTOPPED _ -> +- failwith (Printf.sprintf "Error while running: %s" s) in ++ failwith (Printf.sprintf "Error while running: %s" s_orig) in + let res = try + kont ic + with e -> (close (); raise e) +--- ./src/options.ml ++++ ./src/options.ml +@@ -174,11 +174,24 @@ + build_dir := Filename.concat (Sys.getcwd ()) s + else + build_dir := s ++ ++let slashify = ++ if Sys.win32 then fun p -> String.map (function '\\' -> '/' | x -> x) p ++ else fun p ->p ++ ++let sb () = ++ match Sys.os_type with ++ | "Win32" -> ++ (try set_binary_mode_out stdout true with _ -> ()); ++ | _ -> () ++ ++ + let spec = ref ( + let print_version () = ++ sb (); + Printf.printf "ocamlbuild %s\n%!" Ocamlbuild_config.version; raise Exit_OK + in +- let print_vnum () = print_endline Ocamlbuild_config.version; raise Exit_OK in ++ let print_vnum () = sb (); print_endline Ocamlbuild_config.version; raise Exit_OK in + Arg.align + [ + "-version", Unit print_version , " Display the version"; +@@ -257,8 +270,8 @@ + "-build-dir", String set_build_dir, " Set build directory (implies no-links)"; + "-install-lib-dir", Set_string Ocamlbuild_where.libdir, " Set the install library directory"; + "-install-bin-dir", Set_string Ocamlbuild_where.bindir, " Set the install binary directory"; +- "-where", Unit (fun () -> print_endline !Ocamlbuild_where.libdir; raise Exit_OK), " Display the install library directory"; +- "-which", String (fun cmd -> print_endline (find_tool cmd); raise Exit_OK), " Display path to the tool command"; ++ "-where", Unit (fun () -> sb (); print_endline (slashify !Ocamlbuild_where.libdir); raise Exit_OK), " Display the install library directory"; ++ "-which", String (fun cmd -> sb (); print_endline (slashify (find_tool cmd)); raise Exit_OK), " Display path to the tool command"; + "-ocamlc", set_cmd ocamlc, " Set the OCaml bytecode compiler"; + "-plugin-ocamlc", set_cmd plugin_ocamlc, " Set the OCaml bytecode compiler \ + used when building myocamlbuild.ml (only)"; +--- ./src/pathname.ml ++++ ./src/pathname.ml +@@ -84,6 +84,26 @@ + | x :: xs -> x :: normalize_list xs + + let normalize x = ++ let x = ++ if Sys.win32 = false then ++ x ++ else ++ let len = String.length x in ++ let b = Bytes.create len in ++ for i = 0 to pred len do ++ match x.[i] with ++ | '\\' -> Bytes.set b i '/' ++ | c -> Bytes.set b i c ++ done; ++ if len > 1 then ( ++ let c1 = Bytes.get b 0 in ++ let c2 = Bytes.get b 1 in ++ if c2 = ':' && c1 >= 'a' && c1 <= 'z' && ++ ( len = 2 || Bytes.get b 2 = '/') then ++ Bytes.set b 0 (Char.uppercase_ascii c1) ++ ); ++ Bytes.unsafe_to_string b ++ in + if Glob.eval not_normal_form_re x then + let root, paths = split x in + join root (normalize_list paths) +--- ./src/shell.ml ++++ ./src/shell.ml +@@ -24,12 +24,26 @@ + | 'a'..'z' | 'A'..'Z' | '0'..'9' | '.' | '-' | '/' | '_' | ':' | '@' | '+' | ',' -> loop (pos + 1) + | _ -> false in + loop 0 ++ ++let generic_quote quotequote s = ++ let l = String.length s in ++ let b = Buffer.create (l + 20) in ++ Buffer.add_char b '\''; ++ for i = 0 to l - 1 do ++ if s.[i] = '\'' ++ then Buffer.add_string b quotequote ++ else Buffer.add_char b s.[i] ++ done; ++ Buffer.add_char b '\''; ++ Buffer.contents b ++let unix_quote = generic_quote "'\\''" ++ + let quote_filename_if_needed s = + if is_simple_filename s then s + (* We should probably be using [Filename.unix_quote] except that function + * isn't exported. Users on Windows will have to live with not being able to + * install OCaml into c:\o'caml. Too bad. *) +- else if Sys.os_type = "Win32" then Printf.sprintf "'%s'" s ++ else if Sys.os_type = "Win32" then unix_quote s + else Filename.quote s + let chdir dir = + reset_filesys_cache (); +@@ -37,7 +51,7 @@ + let run args target = + reset_readdir_cache (); + let cmd = String.concat " " (List.map quote_filename_if_needed args) in +- if !*My_unix.is_degraded || Sys.os_type = "Win32" then ++ if !*My_unix.is_degraded then + begin + Log.event cmd target Tags.empty; + let st = sys_command cmd in diff --git a/lib/esy.lock/overrides/opam__s__ocamlbuild_opam__c__0.14.0_opam_override/package.json b/lib/esy.lock/overrides/opam__s__ocamlbuild_opam__c__0.14.0_opam_override/package.json new file mode 100644 index 00000000..b24be7b5 --- /dev/null +++ b/lib/esy.lock/overrides/opam__s__ocamlbuild_opam__c__0.14.0_opam_override/package.json @@ -0,0 +1,27 @@ +{ + "build": [ + [ + "bash", + "-c", + "#{os == 'windows' ? 'patch -p1 < ocamlbuild-0.14.0.patch' : 'true'}" + ], + [ + "make", + "-f", + "configure.make", + "all", + "OCAMLBUILD_PREFIX=#{self.install}", + "OCAMLBUILD_BINDIR=#{self.bin}", + "OCAMLBUILD_LIBDIR=#{self.lib}", + "OCAMLBUILD_MANDIR=#{self.man}", + "OCAMLBUILD_NATIVE=true", + "OCAMLBUILD_NATIVE_TOOLS=true" + ], + [ + "make", + "check-if-preinstalled", + "all", + "#{os == 'windows' ? 'install' : 'opam-install'}" + ] + ] +} diff --git a/lib/esy.lock/overrides/opam__s__ocamlfind_opam__c__1.8.1_opam_override/files/findlib-1.8.1.patch b/lib/esy.lock/overrides/opam__s__ocamlfind_opam__c__1.8.1_opam_override/files/findlib-1.8.1.patch new file mode 100644 index 00000000..3e3ee5a2 --- /dev/null +++ b/lib/esy.lock/overrides/opam__s__ocamlfind_opam__c__1.8.1_opam_override/files/findlib-1.8.1.patch @@ -0,0 +1,471 @@ +--- ./Makefile ++++ ./Makefile +@@ -57,16 +57,16 @@ + cat findlib.conf.in | \ + $(SH) tools/patch '@SITELIB@' '$(OCAML_SITELIB)' >findlib.conf + if ./tools/cmd_from_same_dir ocamlc; then \ +- echo 'ocamlc="ocamlc.opt"' >>findlib.conf; \ ++ echo 'ocamlc="ocamlc.opt$(EXEC_SUFFIX)"' >>findlib.conf; \ + fi + if ./tools/cmd_from_same_dir ocamlopt; then \ +- echo 'ocamlopt="ocamlopt.opt"' >>findlib.conf; \ ++ echo 'ocamlopt="ocamlopt.opt$(EXEC_SUFFIX)"' >>findlib.conf; \ + fi + if ./tools/cmd_from_same_dir ocamldep; then \ +- echo 'ocamldep="ocamldep.opt"' >>findlib.conf; \ ++ echo 'ocamldep="ocamldep.opt$(EXEC_SUFFIX)"' >>findlib.conf; \ + fi + if ./tools/cmd_from_same_dir ocamldoc; then \ +- echo 'ocamldoc="ocamldoc.opt"' >>findlib.conf; \ ++ echo 'ocamldoc="ocamldoc.opt$(EXEC_SUFFIX)"' >>findlib.conf; \ + fi + + .PHONY: install-doc +--- ./src/findlib/findlib_config.mlp ++++ ./src/findlib/findlib_config.mlp +@@ -24,3 +24,5 @@ + | "MacOS" -> "" (* don't know *) + | _ -> failwith "Unknown Sys.os_type" + ;; ++ ++let exec_suffix = "@EXEC_SUFFIX@";; +--- ./src/findlib/findlib.ml ++++ ./src/findlib/findlib.ml +@@ -28,15 +28,20 @@ + let conf_ldconf = ref "";; + let conf_ignore_dups_in = ref ([] : string list);; + +-let ocamlc_default = "ocamlc";; +-let ocamlopt_default = "ocamlopt";; +-let ocamlcp_default = "ocamlcp";; +-let ocamloptp_default = "ocamloptp";; +-let ocamlmklib_default = "ocamlmklib";; +-let ocamlmktop_default = "ocamlmktop";; +-let ocamldep_default = "ocamldep";; +-let ocamlbrowser_default = "ocamlbrowser";; +-let ocamldoc_default = "ocamldoc";; ++let add_exec str = ++ match Findlib_config.exec_suffix with ++ | "" -> str ++ | a -> str ^ a ;; ++let ocamlc_default = add_exec "ocamlc";; ++let ocamlopt_default = add_exec "ocamlopt";; ++let ocamlcp_default = add_exec "ocamlcp";; ++let ocamloptp_default = add_exec "ocamloptp";; ++let ocamlmklib_default = add_exec "ocamlmklib";; ++let ocamlmktop_default = add_exec "ocamlmktop";; ++let ocamldep_default = add_exec "ocamldep";; ++let ocamlbrowser_default = add_exec "ocamlbrowser";; ++let ocamldoc_default = add_exec "ocamldoc";; ++ + + + let init_manually +--- ./src/findlib/fl_package_base.ml ++++ ./src/findlib/fl_package_base.ml +@@ -133,7 +133,15 @@ + List.find (fun def -> def.def_var = "exists_if") p.package_defs in + let files = Fl_split.in_words def.def_value in + List.exists +- (fun file -> Sys.file_exists (Filename.concat d' file)) ++ (fun file -> ++ let fln = Filename.concat d' file in ++ let e = Sys.file_exists fln in ++ (* necessary for ppx executables *) ++ if e || Sys.os_type <> "Win32" || Filename.check_suffix fln ".exe" then ++ e ++ else ++ Sys.file_exists (fln ^ ".exe") ++ ) + files + with Not_found -> true in + +--- ./src/findlib/fl_split.ml ++++ ./src/findlib/fl_split.ml +@@ -126,10 +126,17 @@ + | '/' | '\\' -> true + | _ -> false in + let norm_dir_win() = +- if l >= 1 && s.[0] = '/' then +- Buffer.add_char b '\\' else Buffer.add_char b s.[0]; +- if l >= 2 && s.[1] = '/' then +- Buffer.add_char b '\\' else Buffer.add_char b s.[1]; ++ if l >= 1 then ( ++ if s.[0] = '/' then ++ Buffer.add_char b '\\' ++ else ++ Buffer.add_char b s.[0] ; ++ if l >= 2 then ++ if s.[1] = '/' then ++ Buffer.add_char b '\\' ++ else ++ Buffer.add_char b s.[1]; ++ ); + for k = 2 to l - 1 do + let c = s.[k] in + if is_slash c then ( +--- ./src/findlib/frontend.ml ++++ ./src/findlib/frontend.ml +@@ -31,10 +31,18 @@ + else + Sys_error (arg ^ ": " ^ Unix.error_message code) + ++let is_win = Sys.os_type = "Win32" ++ ++let () = ++ match Findlib_config.system with ++ | "win32" | "win64" | "mingw" | "cygwin" | "mingw64" | "cygwin64" -> ++ (try set_binary_mode_out stdout true with _ -> ()); ++ (try set_binary_mode_out stderr true with _ -> ()); ++ | _ -> () + + let slashify s = + match Findlib_config.system with +- | "mingw" | "mingw64" | "cygwin" -> ++ | "win32" | "win64" | "mingw" | "cygwin" | "mingw64" | "cygwin64" -> + let b = Buffer.create 80 in + String.iter + (function +@@ -49,7 +57,7 @@ + + let out_path ?(prefix="") s = + match Findlib_config.system with +- | "mingw" | "mingw64" | "cygwin" -> ++ | "win32" | "win64" | "mingw" | "mingw64" | "cygwin" -> + let u = slashify s in + prefix ^ + (if String.contains u ' ' then +@@ -273,11 +281,9 @@ + + + let identify_dir d = +- match Sys.os_type with +- | "Win32" -> +- failwith "identify_dir" (* not available *) +- | _ -> +- let s = Unix.stat d in ++ if is_win then ++ failwith "identify_dir"; (* not available *) ++ let s = Unix.stat d in + (s.Unix.st_dev, s.Unix.st_ino) + ;; + +@@ -459,6 +465,96 @@ + ) + packages + ++let rewrite_cmd s = ++ if s = "" || not is_win then ++ s ++ else ++ let s = ++ let l = String.length s in ++ let b = Buffer.create l in ++ for i = 0 to pred l do ++ match s.[i] with ++ | '/' -> Buffer.add_char b '\\' ++ | x -> Buffer.add_char b x ++ done; ++ Buffer.contents b ++ in ++ if (Filename.is_implicit s && String.contains s '\\' = false) || ++ Filename.check_suffix (String.lowercase s) ".exe" then ++ s ++ else ++ let s' = s ^ ".exe" in ++ if Sys.file_exists s' then ++ s' ++ else ++ s ++ ++let rewrite_cmd s = ++ if s = "" || not is_win then s else ++ let s = ++ let l = String.length s in ++ let b = Buffer.create l in ++ for i = 0 to pred l do ++ match s.[i] with ++ | '/' -> Buffer.add_char b '\\' ++ | x -> Buffer.add_char b x ++ done; ++ Buffer.contents b ++ in ++ if (Filename.is_implicit s && String.contains s '\\' = false) || ++ Filename.check_suffix (String.lowercase s) ".exe" then ++ s ++ else ++ let s' = s ^ ".exe" in ++ if Sys.file_exists s' then ++ s' ++ else ++ s ++ ++let rewrite_pp cmd = ++ if not is_win then cmd else ++ let module T = struct exception Keep end in ++ let is_whitespace = function ++ | ' ' | '\011' | '\012' | '\n' | '\r' | '\t' -> true ++ | _ -> false in ++ (* characters that triggers special behaviour (cmd.exe, not unix shell) *) ++ let is_unsafe_char = function ++ | '(' | ')' | '%' | '!' | '^' | '<' | '>' | '&' -> true ++ | _ -> false in ++ let len = String.length cmd in ++ let buf = Buffer.create (len + 4) in ++ let buf_cmd = Buffer.create len in ++ let rec iter_ws i = ++ if i >= len then () else ++ let cur = cmd.[i] in ++ if is_whitespace cur then ( ++ Buffer.add_char buf cur; ++ iter_ws (succ i) ++ ) ++ else ++ iter_cmd i ++ and iter_cmd i = ++ if i >= len then add_buf_cmd () else ++ let cur = cmd.[i] in ++ if is_unsafe_char cur || cur = '"' || cur = '\'' then ++ raise T.Keep; ++ if is_whitespace cur then ( ++ add_buf_cmd (); ++ Buffer.add_substring buf cmd i (len - i) ++ ) ++ else ( ++ Buffer.add_char buf_cmd cur; ++ iter_cmd (succ i) ++ ) ++ and add_buf_cmd () = ++ if Buffer.length buf_cmd > 0 then ++ Buffer.add_string buf (rewrite_cmd (Buffer.contents buf_cmd)) ++ in ++ try ++ iter_ws 0; ++ Buffer.contents buf ++ with ++ | T.Keep -> cmd + + let process_pp_spec syntax_preds packages pp_opts = + (* Returns: pp_command *) +@@ -549,7 +645,7 @@ + None -> [] + | Some cmd -> + ["-pp"; +- cmd ^ " " ^ ++ (rewrite_cmd cmd) ^ " " ^ + String.concat " " (List.map Filename.quote pp_i_options) ^ " " ^ + String.concat " " (List.map Filename.quote pp_archives) ^ " " ^ + String.concat " " (List.map Filename.quote pp_opts)] +@@ -625,9 +721,11 @@ + in + try + let preprocessor = ++ rewrite_cmd ( + resolve_path + ~base ~explicit:true +- (package_property predicates pname "ppx") in ++ (package_property predicates pname "ppx") ) ++ in + ["-ppx"; String.concat " " (preprocessor :: options)] + with Not_found -> [] + ) +@@ -895,6 +993,14 @@ + switch (e.g. -L instead of -L ) + *) + ++(* We may need to remove files on which we do not have complete control. ++ On Windows, removing a read-only file fails so try to change the ++ mode of the file first. *) ++let remove_file fname = ++ try Sys.remove fname ++ with Sys_error _ when is_win -> ++ (try Unix.chmod fname 0o666 with Unix.Unix_error _ -> ()); ++ Sys.remove fname + + let ocamlc which () = + +@@ -1022,9 +1128,12 @@ + + "-intf", + Arg.String (fun s -> pass_files := !pass_files @ [ Intf(slashify s) ]); +- ++ + "-pp", +- Arg.String (fun s -> pp_specified := true; add_spec_fn "-pp" s); ++ Arg.String (fun s -> pp_specified := true; add_spec_fn "-pp" (rewrite_pp s)); ++ ++ "-ppx", ++ Arg.String (fun s -> add_spec_fn "-ppx" (rewrite_pp s)); + + "-thread", + Arg.Unit (fun _ -> threads := threads_default); +@@ -1237,7 +1346,7 @@ + with + any -> + close_out initl; +- Sys.remove initl_file_name; ++ remove_file initl_file_name; + raise any + end; + +@@ -1245,9 +1354,9 @@ + at_exit + (fun () -> + let tr f x = try f x with _ -> () in +- tr Sys.remove initl_file_name; +- tr Sys.remove (Filename.chop_extension initl_file_name ^ ".cmi"); +- tr Sys.remove (Filename.chop_extension initl_file_name ^ ".cmo"); ++ tr remove_file initl_file_name; ++ tr remove_file (Filename.chop_extension initl_file_name ^ ".cmi"); ++ tr remove_file (Filename.chop_extension initl_file_name ^ ".cmo"); + ); + + let exclude_list = [ stdlibdir; threads_dir; vmthreads_dir ] in +@@ -1493,7 +1602,9 @@ + [ "-v", Arg.Unit (fun () -> verbose := Verbose); + "-pp", Arg.String (fun s -> + pp_specified := true; +- options := !options @ ["-pp"; s]); ++ options := !options @ ["-pp"; rewrite_pp s]); ++ "-ppx", Arg.String (fun s -> ++ options := !options @ ["-ppx"; rewrite_pp s]); + ] + ) + ) +@@ -1672,7 +1783,9 @@ + Arg.String (fun s -> add_spec_fn "-I" (slashify (resolve_path s))); + + "-pp", Arg.String (fun s -> pp_specified := true; +- add_spec_fn "-pp" s); ++ add_spec_fn "-pp" (rewrite_pp s)); ++ "-ppx", Arg.String (fun s -> add_spec_fn "-ppx" (rewrite_pp s)); ++ + ] + ) + ) +@@ -1830,7 +1943,10 @@ + output_string ch_out append; + close_out ch_out; + close_in ch_in; +- Unix.utimes outpath s.Unix.st_mtime s.Unix.st_mtime; ++ (try Unix.utimes outpath s.Unix.st_mtime s.Unix.st_mtime ++ with Unix.Unix_error(e,_,_) -> ++ prerr_endline("Warning: setting utimes for " ^ outpath ++ ^ ": " ^ Unix.error_message e)); + + prerr_endline("Installed " ^ outpath); + with +@@ -1882,6 +1998,8 @@ + Unix.openfile (Filename.concat dir owner_file) [Unix.O_RDONLY] 0 in + let f = + Unix.in_channel_of_descr fd in ++ if is_win then ++ set_binary_mode_in f false; + try + let line = input_line f in + let is_my_file = (line = pkg) in +@@ -2208,7 +2326,7 @@ + let lines = read_ldconf !ldconf in + let dlldir_norm = Fl_split.norm_dir dlldir in + let dlldir_norm_lc = string_lowercase_ascii dlldir_norm in +- let ci_filesys = (Sys.os_type = "Win32") in ++ let ci_filesys = is_win in + let check_dir d = + let d' = Fl_split.norm_dir d in + (d' = dlldir_norm) || +@@ -2356,7 +2474,7 @@ + List.iter + (fun file -> + let absfile = Filename.concat dlldir file in +- Sys.remove absfile; ++ remove_file absfile; + prerr_endline ("Removed " ^ absfile) + ) + dll_files +@@ -2365,7 +2483,7 @@ + (* Remove the files from the package directory: *) + if Sys.file_exists pkgdir then begin + let files = Sys.readdir pkgdir in +- Array.iter (fun f -> Sys.remove (Filename.concat pkgdir f)) files; ++ Array.iter (fun f -> remove_file (Filename.concat pkgdir f)) files; + Unix.rmdir pkgdir; + prerr_endline ("Removed " ^ pkgdir) + end +@@ -2415,7 +2533,9 @@ + + + let print_configuration() = ++ let sl = slashify in + let dir s = ++ let s = sl s in + if Sys.file_exists s then + s + else +@@ -2453,27 +2573,27 @@ + if md = "" then "the corresponding package directories" else dir md + ); + Printf.printf "The standard library is assumed to reside in:\n %s\n" +- (Findlib.ocaml_stdlib()); ++ (sl (Findlib.ocaml_stdlib())); + Printf.printf "The ld.conf file can be found here:\n %s\n" +- (Findlib.ocaml_ldconf()); ++ (sl (Findlib.ocaml_ldconf())); + flush stdout + | Some "conf" -> +- print_endline (Findlib.config_file()) ++ print_endline (sl (Findlib.config_file())) + | Some "path" -> +- List.iter print_endline (Findlib.search_path()) ++ List.iter ( fun x -> print_endline (sl x)) (Findlib.search_path()) + | Some "destdir" -> +- print_endline (Findlib.default_location()) ++ print_endline ( sl (Findlib.default_location())) + | Some "metadir" -> +- print_endline (Findlib.meta_directory()) ++ print_endline ( sl (Findlib.meta_directory())) + | Some "metapath" -> + let mdir = Findlib.meta_directory() in + let ddir = Findlib.default_location() in +- print_endline +- (if mdir <> "" then mdir ^ "/META.%s" else ddir ^ "/%s/META") ++ print_endline ( sl ++ (if mdir <> "" then mdir ^ "/META.%s" else ddir ^ "/%s/META")) + | Some "stdlib" -> +- print_endline (Findlib.ocaml_stdlib()) ++ print_endline ( sl (Findlib.ocaml_stdlib())) + | Some "ldconf" -> +- print_endline (Findlib.ocaml_ldconf()) ++ print_endline ( sl (Findlib.ocaml_ldconf())) + | _ -> + assert false + ;; +@@ -2481,7 +2601,7 @@ + + let ocamlcall pkg cmd = + let dir = package_directory pkg in +- let path = Filename.concat dir cmd in ++ let path = rewrite_cmd (Filename.concat dir cmd) in + begin + try Unix.access path [ Unix.X_OK ] + with +@@ -2647,6 +2767,10 @@ + | Sys_error f -> + prerr_endline ("ocamlfind: " ^ f); + exit 2 ++ | Unix.Unix_error (e, fn, f) -> ++ prerr_endline ("ocamlfind: " ^ fn ^ " " ^ f ++ ^ ": " ^ Unix.error_message e); ++ exit 2 + | Findlib.No_such_package(pkg,info) -> + prerr_endline ("ocamlfind: Package `" ^ pkg ^ "' not found" ^ + (if info <> "" then " - " ^ info else "")); +--- ./src/findlib/Makefile ++++ ./src/findlib/Makefile +@@ -90,6 +90,7 @@ + cat findlib_config.mlp | \ + $(SH) $(TOP)/tools/patch '@CONFIGFILE@' '$(OCAMLFIND_CONF)' | \ + $(SH) $(TOP)/tools/patch '@STDLIB@' '$(OCAML_CORE_STDLIB)' | \ ++ $(SH) $(TOP)/tools/patch '@EXEC_SUFFIX@' '$(EXEC_SUFFIX)' | \ + sed -e 's;@AUTOLINK@;$(OCAML_AUTOLINK);g' \ + -e 's;@SYSTEM@;$(SYSTEM);g' \ + >findlib_config.ml diff --git a/lib/esy.lock/overrides/opam__s__ocamlfind_opam__c__1.8.1_opam_override/package.json b/lib/esy.lock/overrides/opam__s__ocamlfind_opam__c__1.8.1_opam_override/package.json new file mode 100644 index 00000000..9314f870 --- /dev/null +++ b/lib/esy.lock/overrides/opam__s__ocamlfind_opam__c__1.8.1_opam_override/package.json @@ -0,0 +1,61 @@ +{ + "build": [ + [ + "bash", + "-c", + "#{os == 'windows' ? 'patch -p1 < findlib-1.8.1.patch' : 'true'}" + ], + [ + "./configure", + "-bindir", + "#{self.bin}", + "-sitelib", + "#{self.lib}", + "-mandir", + "#{self.man}", + "-config", + "#{self.lib}/findlib.conf", + "-no-custom", + "-no-topfind" + ], + [ + "make", + "all" + ], + [ + "make", + "opt" + ] + ], + "install": [ + [ + "make", + "install" + ], + [ + "install", + "-m", + "0755", + "ocaml-stub", + "#{self.bin}/ocaml" + ], + [ + "mkdir", + "-p", + "#{self.toplevel}" + ], + [ + "install", + "-m", + "0644", + "src/findlib/topfind", + "#{self.toplevel}/topfind" + ] + ], + "exportedEnv": { + "OCAML_TOPLEVEL_PATH": { + "val": "#{self.toplevel}", + "scope": "global" + } + } +} diff --git a/lib/package.json b/lib/package.json new file mode 100644 index 00000000..c1c6e915 --- /dev/null +++ b/lib/package.json @@ -0,0 +1,25 @@ +{ + "name": "re-formality", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "yarn run bs:watch", + "bs:build": "bsb -clean-world -make-world", + "bs:watch": "bsb -clean-world -make-world -w", + "bs:install": "bsb -install", + "bs:clean": "bsb -clean-world", + "ppx:build": "esy build", + "test": "esy x test.exe", + "test:output:read": "test/script/output-read", + "test:output:write": "test/script/output-write", + "test:clean": "test/script/clean", + "format": "bsrefmt --in-place **/*.{re,rei}" + }, + "dependencies": { + "re-debouncer": "2.1.0" + }, + "devDependencies": { + "bs-platform": "7.2.2", + "reason-react": "0.7.0" + } +} diff --git a/lib/ppx/Ast.re b/lib/ppx/Ast.re new file mode 100644 index 00000000..10e904f7 --- /dev/null +++ b/lib/ppx/Ast.re @@ -0,0 +1,11 @@ +open Ppxlib; +open Ast_helper; + +let str = (~loc, x: string) => {txt: x, loc}; +let lid = (~loc, x: Longident.t) => {txt: x, loc}; + +let explicit_arity = (~loc) => { + attr_name: "explicit_arity" |> str(~loc), + attr_payload: PStr([]), + attr_loc: Location.none, +}; diff --git a/lib/ppx/AstHelpers.re b/lib/ppx/AstHelpers.re new file mode 100644 index 00000000..e3aab0bc --- /dev/null +++ b/lib/ppx/AstHelpers.re @@ -0,0 +1,349 @@ +open Ast; +open Meta; + +open Ppxlib; +open Ast_helper; + +module T = { + let constructor = (~loc, ~args: option(list(core_type))=?, x) => + Type.constructor( + ~args=? + switch (args) { + | Some(args) => Some(Pcstr_tuple(args)) + | None => None + }, + x |> str(~loc), + ); +}; + +module P = { + let rec or_ = (~pat, ~make, list) => + switch (list) { + | [] => pat + | [x] => x |> make |> Pat.or_(pat) + | [x, ...rest] => rest |> or_(~pat=x |> make |> Pat.or_(pat), ~make) + }; +}; + +module E = { + let some = (~loc, x) => + Exp.construct( + ~attrs=[explicit_arity(~loc)], + Lident("Some") |> lid(~loc), + Some(Exp.tuple([x])), + ); + + let rec seq = (~exp, ~make, list) => + switch (list) { + | [] => exp + | [x] => x |> make |> Exp.sequence(exp) + | [x, ...rest] => + rest |> seq(~exp=x |> make |> Exp.sequence(exp), ~make) + }; + + let rec conj = (~exp, ~make, ~loc, list) => + switch (list) { + | [] => exp + | [x] => + Exp.apply( + Exp.ident(Lident("&&") |> lid(~loc)), + [(Nolabel, exp), (Nolabel, x |> make(~loc))], + ) + | [x, ...rest] => + rest + |> conj( + ~exp= + Exp.apply( + Exp.ident(Lident("&&") |> lid(~loc)), + [(Nolabel, exp), (Nolabel, x |> make(~loc))], + ), + ~make, + ~loc, + ) + }; + + let ref_ = (~loc, x) => + Exp.apply( + Exp.ident(Lident("!") |> lid(~loc)), + [(Nolabel, Exp.ident(Lident(x) |> lid(~loc)))], + ); + + let record = (~loc, xs: list((string, expression))) => + Exp.record( + xs |> List.map(((name, expr)) => (Lident(name) |> lid(~loc), expr)), + None, + ); + + let field = (~in_ as record, ~loc, field) => + Exp.field( + Exp.ident(Lident(record) |> lid(~loc)), + Lident(field) |> lid(~loc), + ); + + let field2 = (~in_ as (record1, record2), ~loc, field) => + Exp.field( + Exp.field( + Exp.ident(Lident(record1) |> lid(~loc)), + Lident(record2) |> lid(~loc), + ), + Lident(field) |> lid(~loc), + ); + + let field3 = (~in_ as (record1, record2, record3), ~loc, field) => + Exp.field( + Exp.field( + Exp.field( + Exp.ident(Lident(record1) |> lid(~loc)), + Lident(record2) |> lid(~loc), + ), + Lident(record3) |> lid(~loc), + ), + Lident(field) |> lid(~loc), + ); + + let field4 = (~in_ as (record1, record2, record3, record4), ~loc, field) => + Exp.field( + Exp.field( + Exp.field( + Exp.field( + Exp.ident(Lident(record1) |> lid(~loc)), + Lident(record2) |> lid(~loc), + ), + Lident(record3) |> lid(~loc), + ), + Lident(record4) |> lid(~loc), + ), + Lident(field) |> lid(~loc), + ); + + let field_of_collection = + (~in_, ~collection: Collection.t, ~loc, field_name) => + Exp.field( + Exp.apply( + [%expr Belt.Array.getUnsafe], + [ + (Nolabel, collection.plural |> field(~in_, ~loc)), + (Nolabel, [%expr index]), + ], + ), + Lident(field_name) |> lid(~loc), + ); + + let field_of_collection2 = + (~in_, ~collection: Collection.t, ~loc, field_name) => + Exp.field( + Exp.apply( + [%expr Belt.Array.getUnsafe], + [ + (Nolabel, collection.plural |> field2(~in_, ~loc)), + (Nolabel, [%expr index]), + ], + ), + Lident(field_name) |> lid(~loc), + ); + + let ref_field = (~in_ as record, ~loc, field) => + Exp.field(record |> ref_(~loc), Lident(field) |> lid(~loc)); + + let ref_field2 = (~in_ as (record1, record2), ~loc, field) => + Exp.field( + Exp.field(record1 |> ref_(~loc), Lident(record2) |> lid(~loc)), + Lident(field) |> lid(~loc), + ); + + let ref_field_of_collection = + (~in_ as record, ~collection: Collection.t, ~loc, field_name) => + Exp.field( + Exp.apply( + [%expr Belt.Array.getUnsafe], + [ + ( + Nolabel, + Exp.field( + record |> ref_(~loc), + Lident(collection.plural) |> lid(~loc), + ), + ), + (Nolabel, [%expr index]), + ], + ), + Lident(field_name) |> lid(~loc), + ); + + let apply_field = (~in_, ~fn, ~args, ~loc) => + Exp.apply(field(~in_, ~loc, fn), args); + + let apply_field2 = (~in_, ~fn, ~args, ~loc) => + Exp.apply(field2(~in_, ~loc, fn), args); + + let apply_field3 = (~in_, ~fn, ~args, ~loc) => + Exp.apply(field3(~in_, ~loc, fn), args); + + let apply_field4 = (~in_, ~fn, ~args, ~loc) => + Exp.apply(field4(~in_, ~loc, fn), args); + + let update_field = (~in_ as record, ~with_ as value, ~loc, field) => + Exp.record( + [(Lident(field) |> lid(~loc), value)], + Some(Exp.ident(Lident(record) |> lid(~loc))), + ); + + let update_field2 = + (~in_ as (record1, record2), ~with_ as value, ~loc, field) => + Exp.record( + [(Lident(field) |> lid(~loc), value)], + Some( + Exp.field( + Exp.ident(Lident(record1) |> lid(~loc)), + Lident(record2) |> lid(~loc), + ), + ), + ); + + let update_field3 = + (~in_ as (record1, record2, record3), ~with_ as value, ~loc, field) => + Exp.record( + [(Lident(field) |> lid(~loc), value)], + Some( + Exp.field( + Exp.field( + Exp.ident(Lident(record1) |> lid(~loc)), + Lident(record2) |> lid(~loc), + ), + Lident(record3) |> lid(~loc), + ), + ), + ); + + let update_ref_field = (~in_ as record, ~with_ as value, ~loc, field) => + Exp.record( + [(Lident(field) |> lid(~loc), value)], + Some(record |> ref_(~loc)), + ); + + let update_ref_field2 = + (~in_ as (record1, record2), ~with_ as value, ~loc, field) => + Exp.record( + [(Lident(field) |> lid(~loc), value)], + Some( + Exp.field(record1 |> ref_(~loc), Lident(record2) |> lid(~loc)), + ), + ); + + let update_field_of_collection = + ( + ~in_ as record, + ~collection: Collection.t, + ~with_ as value, + ~loc, + field_name, + ) => + Exp.record( + [ + ( + Lident(collection.plural) |> lid(~loc), + Exp.apply( + [%expr Belt.Array.mapWithIndex], + [ + (Nolabel, collection.plural |> field(~in_=record, ~loc)), + ( + Nolabel, + [%expr + (index', item) => + if (index' == index) { + %e + field_name + |> update_field(~in_="item", ~with_=value, ~loc); + } else { + item; + } + ], + ), + ], + ), + ), + ], + Some(Exp.ident(Lident(record) |> lid(~loc))), + ); + + let update_field_of_collection2 = + ( + ~in_ as (record1, record2), + ~collection: Collection.t, + ~with_ as value, + ~loc, + field_name, + ) => + Exp.record( + [ + ( + Lident(collection.plural) |> lid(~loc), + Exp.apply( + [%expr Belt.Array.mapWithIndex], + [ + ( + Nolabel, + collection.plural |> field2(~in_=(record1, record2), ~loc), + ), + ( + Nolabel, + [%expr + (index', item) => + if (index' == index) { + %e + field_name + |> update_field(~in_="item", ~with_=value, ~loc); + } else { + item; + } + ], + ), + ], + ), + ), + ], + Some(record2 |> field(~in_=record1, ~loc)), + ); + + let update_ref_field_of_collection = + ( + ~in_ as record, + ~collection: Collection.t, + ~with_ as value, + ~index_token="index", + ~loc, + field_name, + ) => + Exp.record( + [ + ( + Lident(collection.plural) |> lid(~loc), + Exp.apply( + [%expr Belt.Array.mapWithIndex], + [ + (Nolabel, collection.plural |> ref_field(~in_=record, ~loc)), + ( + Nolabel, + [%expr + (idx_, item) => + if (idx_ + == [%e Exp.ident(Lident(index_token) |> lid(~loc))]) { + %e + field_name + |> update_field(~in_="item", ~with_=value, ~loc); + } else { + item; + } + ], + ), + ], + ), + ), + ], + Some(record |> ref_(~loc)), + ); + + let field_of_collection_validator = + (~validators, ~collection: Collection.t, ~loc, field) => + field |> field3(~in_=(validators, collection.plural, "fields"), ~loc); +}; diff --git a/lib/ppx/Form.re b/lib/ppx/Form.re new file mode 100644 index 00000000..3ec3407e --- /dev/null +++ b/lib/ppx/Form.re @@ -0,0 +1,426 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ext = + Extension.declare( + "form", + Extension.Context.module_expr, + Ast_pattern.__, + (~loc, ~path as _, expr) => { + switch (expr) { + | PStr(structure) => + switch (structure |> Metadata.make) { + | Ok({ + scheme, + async, + output_type, + message_type, + submission_error_type, + validators_record, + debounce_interval, + }) => + // Once all required metadata is gathered and ensured that requirements are met + // We need to iterate over user provided config and do the following: + // 1. Disable some compiler warnings + // 2. Open Formality module at the top of the generated module + // 3. Inject types and values that either optional and weren't provided + // or just generated by ppx + // 4. Update validators record (see Form_ValidatorsRecord for details) + // 5. Append neccessary functions including useForm hook + // + // The strategy would be to find structure_item which contains + // validators record and prepend types and values right before it. + // Then prepend `open Formality` at the top & append functions + // to the result list so those are at the bottom of the module. + let head = [ + Form_Attributes.ast(~loc), + Form_OpenFormality.ast(~loc), + ]; + + let types = { + let types = + ref([ + Form_FieldsStatusesType.ast(~scheme, ~loc), + Form_CollectionsStatusesType.ast(~scheme, ~loc), + Form_StateType.ast(~loc), + Form_ActionType.ast(~scheme, ~loc), + Form_ValidatorsType.ast(~scheme, ~loc), + Form_InterfaceType.ast(~scheme, ~async, ~loc), + ]); + switch (submission_error_type) { + | None => types := [SubmissionErrorType.default(~loc), ...types^] + | Some () => () + }; + switch (message_type) { + | None => types := [MessageType.default(~loc), ...types^] + | Some () => () + }; + switch (output_type) { + | NotProvided => types := [OutputType.default(~loc), ...types^] + | AliasOfInput + | Record(_) => () + }; + types^; + }; + + let values = { + let values = ref([]); + switch (debounce_interval) { + | None when async => + values := [DebounceInterval.default(~loc), ...values^] + | None + | Some () => () + }; + values^; + }; + + let funcs = [ + Form_InitialFieldsStatusesFn.ast(~scheme, ~loc), + Form_InitialCollectionsStatuses.ast(~scheme, ~loc), + Form_InitialStateFn.ast(~loc), + async + ? Form_ValidateFormFn.Async.ast(~scheme, ~loc) + : Form_ValidateFormFn.Sync.ast(~scheme, ~loc), + Form_UseFormFn.ast(~scheme, ~async, ~loc), + ]; + + let structure: structure = + List.fold_right( + (structure_item: structure_item, acc) => + switch (structure_item) { + | {pstr_desc: Pstr_value(rec_flag, value_bindings), pstr_loc} => + let (value_bindings, search_result) = + List.fold_right( + (value, (acc, res: [ | `Found | `NotFound])) => + switch (value) { + | { + pvb_pat: {ppat_desc: Ppat_var({txt: "validators"})}, + } as value => ( + [ + value + |> Form_ValidatorsRecord.ast( + scheme, + validators_record, + ), + ...acc, + ], + `Found, + ) + | _ as value => ([value, ...acc], res) + }, + value_bindings, + ([], `NotFound), + ); + let structure_item = { + pstr_desc: Pstr_value(rec_flag, value_bindings), + pstr_loc, + }; + switch (search_result) { + | `NotFound => [structure_item, ...acc] + | `Found => + List.append( + List.append(types, values), + [structure_item, ...acc], + ) + }; + | _ => [structure_item, ...acc] + }, + structure, + funcs, + ); + + Mod.mk(Pmod_structure(List.append(head, structure))); + + | Error(InputTypeParseError(NotFound)) => + Location.raise_errorf(~loc, "`input` type not found") + | Error(InputTypeParseError(NotRecord(loc))) => + Location.raise_errorf(~loc, "`input` must be of record type") + | Error( + InputTypeParseError( + InvalidAttributes( + InvalidAsyncField(InvalidPayload(loc)) | + InvalidCollectionField(InvalidAsyncField(InvalidPayload(loc))), + ), + ), + ) => + Location.raise_errorf( + ~loc, + "`@field.async` attribute accepts only optional record `{mode: OnChange | OnBlur}`", + ) + | Error( + InputTypeParseError( + InvalidAttributes( + InvalidAsyncField(InvalidAsyncMode(loc)) | + InvalidCollectionField( + InvalidAsyncField(InvalidAsyncMode(loc)), + ), + ), + ), + ) => + Location.raise_errorf( + ~loc, + "Invalid async mode. Use either `OnChange` or `OnBlur`.", + ) + | Error( + InputTypeParseError( + InvalidAttributes( + InvalidFieldDeps(DepsParseError(loc)) | + InvalidCollectionField(InvalidFieldDeps(DepsParseError(loc))), + ), + ), + ) => + Location.raise_errorf( + ~loc, + "`@field.deps` attribute must contain field or tuple of fields", + ) + | Error( + InputTypeParseError( + InvalidAttributes( + InvalidFieldDeps(DepNotFound(dep)) | + InvalidCollectionField(InvalidFieldDeps(DepNotFound(dep))), + ), + ), + ) => + let (field, loc) = + switch (dep) { + | UnvalidatedDepField({name, loc}) => (name, loc) + | UnvalidatedDepFieldOfCollection({collection, field, f_loc}) => ( + collection ++ "." ++ field, + f_loc, + ) + }; + Location.raise_errorf( + ~loc, + "Field `%s` doesn't exist in input", + field, + ); + | Error( + InputTypeParseError( + InvalidAttributes( + InvalidFieldDeps(DepOfItself(`Field(name, loc))) | + InvalidCollectionField( + InvalidFieldDeps(DepOfItself(`Field(name, loc))), + ), + ), + ), + ) => + Location.raise_errorf(~loc, "Field `%s` depends on itself", name) + | Error( + InputTypeParseError( + InvalidAttributes( + InvalidFieldDeps(DepDuplicate(dep)) | + InvalidCollectionField(InvalidFieldDeps(DepDuplicate(dep))), + ), + ), + ) => + let (field, loc) = + switch (dep) { + | UnvalidatedDepField({name, loc}) => (name, loc) + | UnvalidatedDepFieldOfCollection({collection, field, f_loc}) => ( + collection ++ "." ++ field, + f_loc, + ) + }; + Location.raise_errorf( + ~loc, + "Field `%s` is already declared as a dependency for this field", + field, + ); + | Error( + InputTypeParseError( + InvalidAttributes(Conflict(`AsyncWithCollection(loc))), + ), + ) => + Location.raise_errorf( + ~loc, + "Collection can not be `async`. If you want to make specific fields in collection async, you can apply `field.async` attribute to specific fields.", + ) + | Error( + InputTypeParseError( + InvalidAttributes(Conflict(`DepsWithCollection(loc))), + ), + ) => + Location.raise_errorf(~loc, "Collection can not have deps") + | Error( + InputTypeParseError( + InvalidAttributes(InvalidCollectionField(NotArray(loc))), + ), + ) => + Location.raise_errorf(~loc, "Collection must be an array of records") + | Error( + InputTypeParseError( + InvalidAttributes(InvalidCollectionField(InvalidTypeRef(loc))), + ), + ) => + Location.raise_errorf( + ~loc, + "Collection must be an array of records. Record type of collection entry must be defined within this module.", + ) + | Error( + InputTypeParseError( + InvalidAttributes(InvalidCollectionField(RecordNotFound(loc))), + ), + ) => + Location.raise_errorf( + ~loc, + "This type can not be found. Record type of collection entry must be defined within this module.", + ) + | Error( + InputTypeParseError( + InvalidAttributes(InvalidCollectionField(NotRecord(loc))), + ), + ) => + Location.raise_errorf( + ~loc, + "Type of collection entry must be a record", + ) + | Error(OutputTypeParseError(NotRecord(loc))) => + Location.raise_errorf( + ~loc, + "`output` must be of record type or an alias of `input`", + ) + | Error(OutputTypeParseError(BadTypeAlias({alias, loc}))) => + Location.raise_errorf( + ~loc, + "`output` can only be an alias of `input` type or a record", + ) + | Error(OutputTypeParseError(InputNotAvailable(loc))) => + Location.raise_errorf( + ~loc, + "`input` type is not found or in invalid state", + ) + | Error( + OutputTypeParseError( + OutputCollectionNotFound({input_collection, loc}), + ), + ) => + Location.raise_errorf( + ~loc, + "`output` type for %s collection that is defined in `input` is not found or in invalid state", + input_collection.plural, + ) + | Error( + OutputTypeParseError( + InvalidCollection( + InvalidCollectionTypeRef(loc) | CollectionTypeNotRecord(loc) | + CollectionTypeNotFound(loc) | + CollectionOutputNotArray(loc), + ), + ), + ) => + Location.raise_errorf( + ~loc, + "Collection must be an array of records. Record type of collection entry must be defined within this module.", + ) + | Error(ValidatorsRecordParseError(NotFound)) => + Location.raise_errorf(~loc, "`validators` record not found") + | Error( + ValidatorsRecordParseError(NotRecord(loc) | RecordParseError(loc)), + ) => + Location.raise_errorf( + ~loc, + "Failed to parse `validators` record. Please, file an issue with your use-case.", + ) + | Error(ValidatorsRecordParseError(BadTypeAnnotation(loc))) => + Location.raise_errorf( + ~loc, + "`validators` binding must be of `validators` type. You can safely remove type annotation and it will be annotated for you under the hood.", + ) + | Error( + ValidatorsRecordParseError( + ValidatorError( + `BadRequiredValidator(field, `Some(loc) | `None(loc), reason), + ), + ), + ) => + switch (reason) { + | `DifferentIO(input_type, output_type) => + Location.raise_errorf( + ~loc, + "Validator for `%s` field is required because its input and output types are different. So validator function is required to produce value of output type from an input value.", + switch (field) { + | ValidatedInputField(field) => field.name + | ValidatedInputFieldOfCollection({collection, field}) => + collection.singular ++ "." ++ field.name + }, + ) + | `IncludedInDeps(in_deps_of_field) => + Location.raise_errorf( + ~loc, + "Validator for `%s` field is required because this field is included in deps of `%s` field", + switch (field) { + | ValidatedInputField(field) => field.name + | ValidatedInputFieldOfCollection({collection, field}) => + collection.singular ++ "." ++ field.name + }, + switch (in_deps_of_field) { + | ValidatedInputField(field) => field.name + | ValidatedInputFieldOfCollection({collection, field}) => + collection.singular ++ "." ++ field.name + }, + ) + } + | Error(IOMismatch(OutputFieldsNotInInput({fields}))) => + switch (fields) { + | [] => + failwith( + "Empty list of non-matched fields in IOMatchError(OutputFieldsNotInInput)", + ) + | [field] + | [field, ..._] => + Location.raise_errorf( + ~loc, + "`output` field `%s` doesn't exist in `input` type", + switch (field) { + | OutputField(field) => field.name + | OutputFieldOfCollection({collection, field}) => + collection.singular ++ "." ++ field.name + }, + ) + } + | Error(IOMismatch(InputFieldsNotInOutput({fields, loc}))) + | Error( + IOMismatch( + Both({ + input_fields_not_in_output: fields, + output_fields_not_in_input: _, + loc, + }), + ), + ) => + switch (fields) { + | [] => + failwith("Empty list of non-matched fields in IOMatchError(Both)") + | [field] => + Location.raise_errorf( + ~loc, + "`input` field `%s` doesn't exist in `output` type", + switch (field) { + | ValidatedInputField(field) => field.name + | ValidatedInputFieldOfCollection({collection, field}) => + collection.singular ++ "." ++ field.name + }, + ) + | fields => + Location.raise_errorf( + ~loc, + "Some `input` fields don't exist in `output` type: %s", + fields + |> List.map((field: InputField.validated) => + switch (field) { + | ValidatedInputField(field) => field.name + | ValidatedInputFieldOfCollection({collection, field}) => + collection.singular ++ "." ++ field.name + } + ) + |> String.concat(", "), + ) + } + } + | _ => Location.raise_errorf(~loc, "Must be a structure") + } + }); diff --git a/lib/ppx/Form_ActionType.re b/lib/ppx/Form_ActionType.re new file mode 100644 index 00000000..d0778a5f --- /dev/null +++ b/lib/ppx/Form_ActionType.re @@ -0,0 +1,182 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let ast = (~scheme: Scheme.t, ~loc) => { + let update_actions = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(field) => [ + FieldPrinter.update_action(~field=field.name) + |> T.constructor(~args=[[%type: input => input]], ~loc), + ...acc, + ] + | Collection({collection, fields}) => + fields + |> List.fold_left( + (acc, field: Scheme.field) => + [ + FieldOfCollectionPrinter.update_action( + ~collection, + ~field=field.name, + ) + |> T.constructor( + ~args=[[%type: input => input], [%type: index]], + ~loc, + ), + ...acc, + ], + acc, + ) + }, + [], + ); + + let blur_actions = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(field) => [ + FieldPrinter.blur_action(~field=field.name) + |> T.constructor(~loc), + ...acc, + ] + | Collection({collection, fields}) => + List.fold_right( + (field: Scheme.field, acc) => + [ + FieldOfCollectionPrinter.blur_action( + ~collection, + ~field=field.name, + ) + |> T.constructor(~args=[[%type: index]], ~loc), + ...acc, + ], + fields, + acc, + ) + }, + [], + ); + + let apply_async_result_actions = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field({validator: SyncValidator(_)}) => acc + | Field({validator: AsyncValidator(_)} as field) => [ + FieldPrinter.apply_async_result_action(~field=field.name) + |> T.constructor( + ~args=[ + field.output_type |> ItemType.unpack, + Typ.constr( + Lident("result") |> lid(~loc), + [ + field.output_type |> ItemType.unpack, + Typ.constr(Lident("message") |> lid(~loc), []), + ], + ), + ], + ~loc, + ), + ...acc, + ] + | Collection({collection, fields}) => + fields + |> List.fold_left( + (acc, field: Scheme.field) => + switch (field) { + | {validator: SyncValidator(_)} => acc + | {validator: AsyncValidator(_)} as field => [ + FieldOfCollectionPrinter.apply_async_result_action( + ~collection, + ~field=field.name, + ) + |> T.constructor( + ~args=[ + field.output_type |> ItemType.unpack, + [%type: index], + Typ.constr( + Lident("result") |> lid(~loc), + [ + field.output_type |> ItemType.unpack, + Typ.constr( + Lident("message") |> lid(~loc), + [], + ), + ], + ), + ], + ~loc, + ), + ...acc, + ] + }, + acc, + ) + }, + [], + ); + + let collections_actions = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(_) => acc + | Collection({collection, input_type}) => [ + collection + |> CollectionPrinter.add_action + |> T.constructor(~args=[input_type |> ItemType.unpack], ~loc), + collection + |> CollectionPrinter.remove_action + |> T.constructor(~args=[[%type: index]], ~loc), + ...acc, + ] + }, + [], + ); + + let rest_actions = [ + "Submit" |> T.constructor(~loc), + "SetSubmittedStatus" + |> T.constructor(~args=[[%type: option(input)]], ~loc), + "SetSubmissionFailedStatus" + |> T.constructor(~args=[[%type: submissionError]], ~loc), + "MapSubmissionError" + |> T.constructor( + ~args=[[%type: submissionError => submissionError]], + ~loc, + ), + "DismissSubmissionError" |> T.constructor(~loc), + "DismissSubmissionResult" |> T.constructor(~loc), + "Reset" |> T.constructor(~loc), + ]; + + Str.type_( + ~loc, + Recursive, + [ + "action" + |> str(~loc) + |> Type.mk( + ~kind= + Ptype_variant( + rest_actions + |> List.append(collections_actions) + |> List.append(apply_async_result_actions) + |> List.append(blur_actions) + |> List.append(update_actions), + ), + ), + ], + ); +}; diff --git a/lib/ppx/Form_Attributes.re b/lib/ppx/Form_Attributes.re new file mode 100644 index 00000000..f91cb56a --- /dev/null +++ b/lib/ppx/Form_Attributes.re @@ -0,0 +1,33 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let warnings = + [ + // What: + // Warns on spread in single field record: + // type t = {a: int}; + // let inc = x => {...x, a: x.a + 1}; + // Why disabled: + // Handling this introduces more complexity for no profit + // since it's purely stylistic thing and doesn't affect JS output. + "-23", + // What: + // Warns if some type is not in scope and if type annotation + // would be removed, meaning of code might change. + // Why disabled: + // We already have all type annotations in place + // so opening `Async` module everywhere where it's required + // is more work to do for little benefit. Disabling for now. + "-40", + // Legacy + "-42", + ] + |> List.fold_left((acc, x) => acc ++ x, ""); + +let ast = (~loc) => [%stri + [@ocaml.warning [%e warnings |> Const.string |> Exp.constant]] +]; diff --git a/lib/ppx/Form_CollectionsStatusesType.re b/lib/ppx/Form_CollectionsStatusesType.re new file mode 100644 index 00000000..7947c649 --- /dev/null +++ b/lib/ppx/Form_CollectionsStatusesType.re @@ -0,0 +1,40 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let ast = (~scheme: Scheme.t, ~loc) => { + switch (scheme |> Scheme.collections) { + | [] => [%stri type collectionsStatuses = unit] + | collections => + Str.type_( + ~loc, + Recursive, + [ + "collectionsStatuses" + |> str(~loc) + |> Type.mk( + ~kind= + Ptype_record( + collections + |> List.map(({collection, validator}: Scheme.collection) => + Type.field( + collection.plural |> str(~loc), + switch (validator) { + | Ok(Some(_)) + | Error () => [%type: + option(collectionStatus(message)) + ] + | Ok(None) => [%type: unit] + }, + ) + ), + ), + ), + ], + ) + }; +}; diff --git a/lib/ppx/Form_FieldsStatusesType.re b/lib/ppx/Form_FieldsStatusesType.re new file mode 100644 index 00000000..ded0440c --- /dev/null +++ b/lib/ppx/Form_FieldsStatusesType.re @@ -0,0 +1,77 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let field_type = (~loc, field: Scheme.field) => + Type.field( + field.name |> str(~loc), + switch (field.validator) { + | SyncValidator(_) => [%type: + fieldStatus([%t field.output_type |> ItemType.unpack], message) + ] + | AsyncValidator(_) => [%type: + Async.fieldStatus([%t field.output_type |> ItemType.unpack], message) + ] + }, + ); + +let collection_type = (~loc, collection: Collection.t) => + Type.field( + collection.plural |> str(~loc), + [%type: + array( + [%t + Typ.constr( + Lident(collection |> CollectionPrinter.fields_statuses_type) + |> lid(~loc), + [], + ) + ], + ) + ], + ); + +let ast = (~scheme: Scheme.t, ~loc) => { + let main_decl = + "fieldsStatuses" + |> str(~loc) + |> Type.mk( + ~kind= + Ptype_record( + scheme + |> List.map((entry: Scheme.entry) => + switch (entry) { + | Field(field) => field |> field_type(~loc) + | Collection({collection}) => + collection |> collection_type(~loc) + } + ), + ), + ); + + let collections_decls = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(_) => acc + | Collection({collection, fields}) => [ + collection + |> CollectionPrinter.fields_statuses_type + |> str(~loc) + |> Type.mk( + ~kind= + Ptype_record(fields |> List.map(field_type(~loc))), + ), + ...acc, + ] + }, + [], + ); + + Str.type_(~loc, Recursive, [main_decl, ...collections_decls]); +}; diff --git a/lib/ppx/Form_InitialCollectionsStatuses.re b/lib/ppx/Form_InitialCollectionsStatuses.re new file mode 100644 index 00000000..53e7d2e3 --- /dev/null +++ b/lib/ppx/Form_InitialCollectionsStatuses.re @@ -0,0 +1,37 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let ast = (~scheme: Scheme.t, ~loc) => { + [%stri + let initialCollectionsStatuses: collectionsStatuses = + switch%e (scheme |> Scheme.collections) { + | [] => + %expr + () + | collections => + Exp.record( + collections + |> List.map(({collection, validator}: Scheme.collection) => + ( + Lident(collection.plural) |> lid(~loc), + switch (validator) { + | Ok(Some ()) + | Error () => + %expr + None + | Ok(None) => + %expr + () + }, + ) + ), + None, + ) + } + ]; +}; diff --git a/lib/ppx/Form_InitialFieldsStatusesFn.re b/lib/ppx/Form_InitialFieldsStatusesFn.re new file mode 100644 index 00000000..66dbec0e --- /dev/null +++ b/lib/ppx/Form_InitialFieldsStatusesFn.re @@ -0,0 +1,67 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let ast = (~scheme: Scheme.t, ~loc) => { + [%stri + let initialFieldsStatuses = + ( + [%p + switch (scheme |> Scheme.collections) { + | [] => [%pat? _input] + | _ => [%pat? input] + } + ]: input, + ) + : fieldsStatuses => [%e + Exp.record( + scheme + |> List.map((entry: Scheme.entry) => + switch (entry) { + | Field(field) => ( + Lident(field.name) |> lid(~loc), + [%expr Pristine], + ) + | Collection({collection, fields}) => ( + Lident(collection.plural) |> lid(~loc), + [%expr + Belt.Array.make( + Belt.Array.length( + [%e collection.plural |> E.field(~in_="input", ~loc)], + ), + [%e + Exp.constraint_( + Exp.record( + fields + |> List.map((field: Scheme.field) => + ( + Lident(field.name) |> lid(~loc), + [%expr Pristine], + ) + ), + None, + ), + Typ.constr( + Lident( + collection + |> CollectionPrinter.fields_statuses_type, + ) + |> lid(~loc), + [], + ), + ) + ], + ) + ], + ) + } + ), + None, + ) + ] + ]; +}; diff --git a/lib/ppx/Form_InitialStateFn.re b/lib/ppx/Form_InitialStateFn.re new file mode 100644 index 00000000..a1ab6182 --- /dev/null +++ b/lib/ppx/Form_InitialStateFn.re @@ -0,0 +1,16 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = (~loc) => [%stri + let initialState = input => { + input, + fieldsStatuses: input->initialFieldsStatuses, + collectionsStatuses: initialCollectionsStatuses, + formStatus: Editing, + submissionStatus: NeverSubmitted, + } +]; diff --git a/lib/ppx/Form_InterfaceType.re b/lib/ppx/Form_InterfaceType.re new file mode 100644 index 00000000..9ec2cf37 --- /dev/null +++ b/lib/ppx/Form_InterfaceType.re @@ -0,0 +1,225 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let ast = (~scheme: Scheme.t, ~async: bool, ~loc) => { + let f = (x, t) => t |> Type.field(x |> str(~loc)); + + let base = [ + f("input", [%type: input]), + f("status", [%type: formStatus(submissionError)]), + f("dirty", [%type: unit => bool]), + f( + "valid", + async ? [%type: unit => option(bool)] : [%type: unit => bool], + ), + f("submitting", [%type: bool]), + f("submit", [%type: unit => unit]), + f("dismissSubmissionError", [%type: unit => unit]), + f("dismissSubmissionResult", [%type: unit => unit]), + f( + "mapSubmissionError", + [%type: (submissionError => submissionError) => unit], + ), + f("reset", [%type: unit => unit]), + ]; + + let update_fns = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(field) => [ + f( + FieldPrinter.update_fn(~field=field.name), + [%type: (input => input) => unit], + ), + ...acc, + ] + | Collection({collection, fields}) => + fields + |> List.fold_left( + (acc, field: Scheme.field) => + [ + f( + FieldOfCollectionPrinter.update_fn( + ~collection, + ~field=field.name, + ), + [%type: (input => input, ~at: index) => unit], + ), + ...acc, + ], + acc, + ) + }, + [], + ); + + let blur_fns = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(field) => [ + f( + FieldPrinter.blur_fn(~field=field.name), + [%type: unit => unit], + ), + ...acc, + ] + | Collection({collection, fields}) => + fields + |> List.fold_left( + (acc, field: Scheme.field) => + [ + f( + FieldOfCollectionPrinter.blur_fn( + ~collection, + ~field=field.name, + ), + [%type: (~at: index) => unit], + ), + ...acc, + ], + acc, + ) + }, + [], + ); + + let result_entries = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(field) => [ + f( + FieldPrinter.result_value(~field=field.name), + switch (field.validator) { + | SyncValidator(_) => [%type: + option( + result( + [%t field.output_type |> ItemType.unpack], + message, + ), + ) + ] + | AsyncValidator(_) => [%type: + option( + Async.exposedFieldStatus( + [%t field.output_type |> ItemType.unpack], + message, + ), + ) + ] + }, + ), + ...acc, + ] + | Collection({collection, fields}) => + fields + |> List.fold_left( + (acc, field: Scheme.field) => + [ + f( + FieldOfCollectionPrinter.result_fn( + ~collection, + ~field=field.name, + ), + switch (field.validator) { + | SyncValidator(_) => [%type: + (~at: index) => + option( + result( + [%t field.output_type |> ItemType.unpack], + message, + ), + ) + ] + | AsyncValidator(_) => [%type: + (~at: index) => + option( + Async.exposedFieldStatus( + [%t field.output_type |> ItemType.unpack], + message, + ), + ) + ] + }, + ), + ...acc, + ], + acc, + ) + }, + [], + ); + + let collection_entries = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(_) => acc + | Collection({collection, validator, input_type}) => + let add_fn = + f( + collection |> CollectionPrinter.add_fn, + [%type: [%t input_type |> ItemType.unpack] => unit], + ); + let remove_fn = + f( + collection |> CollectionPrinter.remove_fn, + [%type: (~at: index) => unit], + ); + + let result_value = + switch (validator) { + | Ok(Some ()) + | Error () => + Some( + f( + collection |> CollectionPrinter.result_value, + [%type: option(collectionStatus(message))], + ), + ) + | Ok(None) => None + }; + + switch (result_value) { + | Some(result_value) => [ + add_fn, + remove_fn, + result_value, + ...acc, + ] + | None => [add_fn, remove_fn, ...acc] + }; + }, + [], + ); + + Str.type_( + ~loc, + Recursive, + [ + "interface" + |> str(~loc) + |> Type.mk( + ~kind= + Ptype_record( + base + |> List.append(collection_entries) + |> List.append(result_entries) + |> List.append(blur_fns) + |> List.append(update_fns), + ), + ), + ], + ); +}; diff --git a/lib/ppx/Form_OpenFormality.re b/lib/ppx/Form_OpenFormality.re new file mode 100644 index 00000000..c8e99600 --- /dev/null +++ b/lib/ppx/Form_OpenFormality.re @@ -0,0 +1,8 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = (~loc) => [%stri open Formality]; diff --git a/lib/ppx/Form_StateType.re b/lib/ppx/Form_StateType.re new file mode 100644 index 00000000..53f6e474 --- /dev/null +++ b/lib/ppx/Form_StateType.re @@ -0,0 +1,16 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = (~loc) => [%stri + type state = { + input, + fieldsStatuses, + collectionsStatuses, + formStatus: formStatus(submissionError), + submissionStatus, + } +]; diff --git a/lib/ppx/Form_UseFormFn.re b/lib/ppx/Form_UseFormFn.re new file mode 100644 index 00000000..52266f57 --- /dev/null +++ b/lib/ppx/Form_UseFormFn.re @@ -0,0 +1,44 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = (~scheme: Scheme.t, ~async: bool, ~loc) => [%stri + let useForm = + ( + ~initialInput: input, + ~onSubmit: + (output, submissionCallbacks(input, submissionError)) => unit, + ) => { + let memoizedInitialState = + React.useMemo1(() => initialInput->initialState, [|initialInput|]); + + let (state, dispatch) = + ReactUpdate.( + memoizedInitialState->useReducer((state, action) => { + %e + { + Exp.match( + [%expr action], + Form_UseFormFn_RestActions.ast(~loc, ~async) + |> List.append( + Form_UseFormFn_CollectionsActions.ast(~loc, scheme), + ) + |> List.append( + Form_UseFormFn_ApplyAsyncResultActions.ast(~loc, scheme), + ) + |> List.append(Form_UseFormFn_BlurActions.ast(~loc, scheme)) + |> List.append(Form_UseFormFn_UpdateActions.ast(~loc, scheme)), + ); + } + }) + ); + + %e + { + Form_UseFormFn_Interface.ast(~loc, ~async, scheme); + }; + } +]; diff --git a/lib/ppx/Form_UseFormFn_ApplyAsyncResultActions.re b/lib/ppx/Form_UseFormFn_ApplyAsyncResultActions.re new file mode 100644 index 00000000..cdee9ed0 --- /dev/null +++ b/lib/ppx/Form_UseFormFn_ApplyAsyncResultActions.re @@ -0,0 +1,134 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let ast = (~loc, scheme: Scheme.t) => + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field({validator: SyncValidator(_)}) => acc + | Field({validator: AsyncValidator(_)} as field) => [ + Exp.case( + Pat.construct( + Lident( + FieldPrinter.apply_async_result_action(~field=field.name), + ) + |> lid(~loc), + Some( + Pat.tuple([ + Pat.var("value" |> str(~loc)), + Pat.var("result" |> str(~loc)), + ]), + ), + ), + { + %expr + { + let validator = [%e + field.name |> E.field(~in_="validators", ~loc) + ]; + switch ( + [%e + field.name + |> E.field2(~in_=("state", "fieldsStatuses"), ~loc) + ] + ) { + | Validating(x) when validator.eq(x, value) => + Update({ + ...state, + fieldsStatuses: [%e + field.name + |> E.update_field2( + ~in_=("state", "fieldsStatuses"), + ~with_=[%expr Dirty(result, Shown)], + ~loc, + ) + ], + }) + | Validating(_) + | Pristine + | Dirty(_, Shown | Hidden) => NoUpdate + }; + }; + }, + ), + ...acc, + ] + | Collection({collection, fields}) => + fields + |> List.fold_left( + (acc, field: Scheme.field) => + switch (field.validator) { + | SyncValidator(_) => acc + | AsyncValidator(_) => [ + Exp.case( + Pat.construct( + Lident( + FieldOfCollectionPrinter.apply_async_result_action( + ~collection, + ~field=field.name, + ), + ) + |> lid(~loc), + Some( + Pat.tuple([ + Pat.var("value" |> str(~loc)), + Pat.var("index" |> str(~loc)), + Pat.var("result" |> str(~loc)), + ]), + ), + ), + { + %expr + { + let validator = [%e + field.name + |> E.field_of_collection_validator( + ~validators="validators", + ~collection, + ~loc, + ) + ]; + switch ( + [%e + field.name + |> E.field_of_collection2( + ~in_=("state", "fieldsStatuses"), + ~collection, + ~loc, + ) + ] + ) { + | Validating(x) when validator.eq(x, value) => + Update({ + ...state, + fieldsStatuses: [%e + field.name + |> E.update_field_of_collection2( + ~in_=("state", "fieldsStatuses"), + ~collection, + ~with_=[%expr Dirty(result, Shown)], + ~loc, + ) + ], + }) + | Validating(_) + | Pristine + | Dirty(_, Shown | Hidden) => NoUpdate + }; + }; + }, + ), + ...acc, + ] + }, + acc, + ) + }, + [], + ); diff --git a/lib/ppx/Form_UseFormFn_BlurActions.re b/lib/ppx/Form_UseFormFn_BlurActions.re new file mode 100644 index 00000000..ab8fe0c3 --- /dev/null +++ b/lib/ppx/Form_UseFormFn_BlurActions.re @@ -0,0 +1,138 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let ast = (~loc, scheme: Scheme.t) => + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(field) => [ + Exp.case( + Pat.construct( + Lident(FieldPrinter.blur_action(~field=field.name)) + |> lid(~loc), + None, + ), + { + let field_status_expr = + field.name + |> E.field2(~in_=("state", "fieldsStatuses"), ~loc); + let field_input_expr = + field.name |> E.field2(~in_=("state", "input"), ~loc); + let validator_expr = + field.name |> E.field(~in_="validators", ~loc); + let set_status_expr = + field.name + |> E.update_field2( + ~in_=("state", "fieldsStatuses"), + ~with_=[%expr status], + ~loc, + ); + + switch (field.validator) { + | SyncValidator(validator) => + Form_UseFormFn_BlurActions_SyncField.ast( + ~loc, + ~validator, + ~field_status_expr, + ~field_input_expr, + ~validator_expr, + ~set_status_expr, + ) + | AsyncValidator({optionality}) => + Form_UseFormFn_BlurActions_AsyncField.ast( + ~loc, + ~field, + ~optionality, + ~field_status_expr, + ~validator_expr, + ~set_status_expr, + ) + }; + }, + ), + ...acc, + ] + | Collection({collection, fields}) => + fields + |> List.fold_left( + (acc, field: Scheme.field) => + [ + Exp.case( + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident( + FieldOfCollectionPrinter.blur_action( + ~collection, + ~field=field.name, + ), + ) + |> lid(~loc), + Some(Pat.tuple([Pat.var("index" |> str(~loc))])), + ), + { + let field_status_expr = + field.name + |> E.field_of_collection2( + ~in_=("state", "fieldsStatuses"), + ~collection, + ~loc, + ); + let field_input_expr = + field.name + |> E.field_of_collection2( + ~in_=("state", "input"), + ~collection, + ~loc, + ); + let validator_expr = + field.name + |> E.field_of_collection_validator( + ~validators="validators", + ~collection, + ~loc, + ); + let set_status_expr = + field.name + |> E.update_field_of_collection2( + ~in_=("state", "fieldsStatuses"), + ~collection, + ~with_=[%expr status], + ~loc, + ); + + switch (field.validator) { + | SyncValidator(validator) => + Form_UseFormFn_BlurActions_SyncFieldOfCollection.ast( + ~loc, + ~validator, + ~field_status_expr, + ~field_input_expr, + ~validator_expr, + ~set_status_expr, + ) + | AsyncValidator({optionality}) => + Form_UseFormFn_BlurActions_AsyncFieldOfCollection.ast( + ~loc, + ~field, + ~collection, + ~optionality, + ~field_status_expr, + ~validator_expr, + ~set_status_expr, + ) + }; + }, + ), + ...acc, + ], + acc, + ) + }, + [], + ); diff --git a/lib/ppx/Form_UseFormFn_BlurActions_AsyncField.re b/lib/ppx/Form_UseFormFn_BlurActions_AsyncField.re new file mode 100644 index 00000000..e9f2fbff --- /dev/null +++ b/lib/ppx/Form_UseFormFn_BlurActions_AsyncField.re @@ -0,0 +1,85 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = + ( + ~loc, + ~field: Scheme.field, + ~optionality: option(FieldOptionality.t), + ~field_status_expr: expression, + ~validator_expr: expression, + ~set_status_expr: expression, + ) => { + %expr + { + let result = + switch%e (optionality) { + | None => + %expr + { + Async.validateFieldOnBlur( + ~input=state.input, + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(OptionType) => + %expr + { + Async.validateFieldOfOptionTypeOnBlur( + ~input=state.input, + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(StringType) => + %expr + { + Async.validateFieldOfStringTypeOnBlur( + ~input=state.input, + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(OptionStringType) => + %expr + { + Async.validateFieldOfOptionStringTypeOnBlur( + ~input=state.input, + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + }; + + switch (result) { + | None => NoUpdate + | Some(fieldsStatuses) => + switch ([%e field.name |> E.field(~in_="fieldsStatuses", ~loc)]) { + | Validating(value) => + UpdateWithSideEffects( + {...state, fieldsStatuses}, + ({state: _, dispatch}) => { + %e + E.apply_field2( + ~in_=("validators", field.name), + ~fn="validateAsync", + ~args=[(Nolabel, [%expr (value, dispatch)])], + ~loc, + ) + }, + ) + | Pristine + | Dirty(_, Shown | Hidden) => Update({...state, fieldsStatuses}) + } + }; + }; +}; diff --git a/lib/ppx/Form_UseFormFn_BlurActions_AsyncFieldOfCollection.re b/lib/ppx/Form_UseFormFn_BlurActions_AsyncFieldOfCollection.re new file mode 100644 index 00000000..5d06e0cf --- /dev/null +++ b/lib/ppx/Form_UseFormFn_BlurActions_AsyncFieldOfCollection.re @@ -0,0 +1,95 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = + ( + ~loc, + ~field: Scheme.field, + ~collection: Collection.t, + ~optionality: option(FieldOptionality.t), + ~field_status_expr: expression, + ~validator_expr: expression, + ~set_status_expr: expression, + ) => { + %expr + { + let result = + switch%e (optionality) { + | None => + %expr + { + Async.validateFieldOfCollectionOnBlur( + ~input=state.input, + ~index, + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(OptionType) => + %expr + { + Async.validateFieldOfCollectionOfOptionTypeOnBlur( + ~input=state.input, + ~index, + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(StringType) => + %expr + { + Async.validateFieldOfCollectionOfStringTypeOnBlur( + ~input=state.input, + ~index, + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(OptionStringType) => + %expr + { + Async.validateFieldOfCollectionOfOptionStringTypeOnBlur( + ~input=state.input, + ~index, + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + }; + + switch (result) { + | None => NoUpdate + | Some(fieldsStatuses) => + switch ( + [%e + field.name + |> E.field_of_collection(~in_="fieldsStatuses", ~collection, ~loc) + ] + ) { + | Validating(value) => + UpdateWithSideEffects( + {...state, fieldsStatuses}, + ({state: _, dispatch}) => { + %e + E.apply_field4( + ~in_=("validators", collection.plural, "fields", field.name), + ~fn="validateAsync", + ~args=[(Nolabel, [%expr (value, index, dispatch)])], + ~loc, + ) + }, + ) + | Pristine + | Dirty(_, Shown | Hidden) => Update({...state, fieldsStatuses}) + } + }; + }; +}; diff --git a/lib/ppx/Form_UseFormFn_BlurActions_SyncField.re b/lib/ppx/Form_UseFormFn_BlurActions_SyncField.re new file mode 100644 index 00000000..9b02fe5a --- /dev/null +++ b/lib/ppx/Form_UseFormFn_BlurActions_SyncField.re @@ -0,0 +1,44 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = + ( + ~loc, + ~validator: result(FieldValidator.sync, unit), + ~field_status_expr: expression, + ~field_input_expr: expression, + ~validator_expr: expression, + ~set_status_expr: expression, + ) => { + %expr + { + let result = + switch%e (validator) { + | Ok(Required | Optional(Some(_))) + | Error () => + %expr + validateFieldOnBlurWithValidator( + ~input=state.input, + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ) + | Ok(Optional(None)) => + %expr + validateFieldOnBlurWithoutValidator( + ~fieldInput=[%e field_input_expr], + ~fieldStatus=[%e field_status_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ) + }; + + switch (result) { + | Some(fieldsStatuses) => Update({...state, fieldsStatuses}) + | None => NoUpdate + }; + }; +}; diff --git a/lib/ppx/Form_UseFormFn_BlurActions_SyncFieldOfCollection.re b/lib/ppx/Form_UseFormFn_BlurActions_SyncFieldOfCollection.re new file mode 100644 index 00000000..a5c690bc --- /dev/null +++ b/lib/ppx/Form_UseFormFn_BlurActions_SyncFieldOfCollection.re @@ -0,0 +1,45 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = + ( + ~loc, + ~validator: result(FieldValidator.sync, unit), + ~field_status_expr: expression, + ~field_input_expr: expression, + ~validator_expr: expression, + ~set_status_expr: expression, + ) => { + %expr + { + let result = + switch%e (validator) { + | Ok(Required | Optional(Some(_))) + | Error () => + %expr + validateFieldOfCollectionOnBlurWithValidator( + ~input=state.input, + ~index, + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ) + | Ok(Optional(None)) => + %expr + validateFieldOnBlurWithoutValidator( + ~fieldInput=[%e field_input_expr], + ~fieldStatus=[%e field_status_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ) + }; + + switch (result) { + | Some(fieldsStatuses) => Update({...state, fieldsStatuses}) + | None => NoUpdate + }; + }; +}; diff --git a/lib/ppx/Form_UseFormFn_CollectionsActions.re b/lib/ppx/Form_UseFormFn_CollectionsActions.re new file mode 100644 index 00000000..8a9ea4c0 --- /dev/null +++ b/lib/ppx/Form_UseFormFn_CollectionsActions.re @@ -0,0 +1,283 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let ast = (~loc, scheme: Scheme.t) => { + let collections = scheme |> Scheme.collections; + collections + |> List.fold_left( + (acc, {collection, validator, fields}: Scheme.collection) => { + let deps = + fields + |> List.fold_left( + (acc, field: Scheme.field) => acc |> List.append(field.deps), + [], + ); + + let add_action_pat = + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident(collection |> CollectionPrinter.add_action) |> lid(~loc), + Some(Pat.tuple([Pat.var("entry" |> str(~loc))])), + ); + + let add_entry_to_input_exp = + collection.plural + |> E.update_field2( + ~in_=("state", "input"), + ~with_=[%expr + Belt.Array.concat( + [%e + collection.plural + |> E.field2(~in_=("state", "input"), ~loc) + ], + [|entry|], + ) + ], + ~loc, + ); + + let add_entry_to_fields_statuses_exp = + collection.plural + |> E.update_field2( + ~in_=("state", "fieldsStatuses"), + ~with_=[%expr + Belt.Array.concat( + [%e + collection.plural + |> E.field2(~in_=("state", "fieldsStatuses"), ~loc) + ], + [| + [%e + Exp.record( + fields + |> List.map((field: Scheme.field) => + ( + Lident(field.name) |> lid(~loc), + [%expr Pristine], + ) + ), + None, + ) + ], + |], + ) + ], + ~loc, + ); + + let remove_action_pat = + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident(collection |> CollectionPrinter.remove_action) + |> lid(~loc), + Some(Pat.tuple([Pat.var("index" |> str(~loc))])), + ); + + let remove_entry_from_input_exp = + collection.plural + |> E.update_field2( + ~in_=("state", "input"), + ~with_=[%expr + Belt.Array.keepWithIndex( + [%e + collection.plural + |> E.field2(~in_=("state", "input"), ~loc) + ], + (_, i) => + i != index + ) + ], + ~loc, + ); + + let remove_entry_from_fields_statuses_exp = + collection.plural + |> E.update_field2( + ~in_=("state", "fieldsStatuses"), + ~with_=[%expr + Belt.Array.keepWithIndex( + [%e + collection.plural + |> E.field2(~in_=("state", "fieldsStatuses"), ~loc) + ], + (_, i) => + i != index + ) + ], + ~loc, + ); + + let update_collections_statuses = + Exp.record( + [ + ( + Lident(collection.plural) |> lid(~loc), + [%expr + Some( + [%e + E.apply_field2( + ~in_=("validators", collection.plural), + ~fn="collection", + ~args=[(Nolabel, [%expr nextInput])], + ~loc, + ) + ], + ) + ], + ), + ], + switch (collections) { + | [] => None + | [x] => None + | _ => Some([%expr state.collectionsStatuses]) + }, + ); + + [ + Exp.case( + add_action_pat, + switch (deps) { + | [] => + %expr + { + let nextInput = [%e add_entry_to_input_exp]; + let nextFieldsStatuses = [%e + add_entry_to_fields_statuses_exp + ]; + switch%e (validator) { + | Ok(Some ()) + | Error () => + %expr + Update({ + ...state, + input: nextInput, + fieldsStatuses: nextFieldsStatuses, + collectionsStatuses: [%e update_collections_statuses], + }) + | Ok(None) => + %expr + Update({ + ...state, + input: nextInput, + fieldsStatuses: nextFieldsStatuses, + }) + }; + } + | [dep, ...deps] => + %expr + { + let nextInput = [%e add_entry_to_input_exp]; + let nextFieldsStatuses = + ref([%e add_entry_to_fields_statuses_exp]); + + %e + { + scheme + |> Form_UseFormFn_DependentFields.ast( + ~loc, + ~dep, + ~deps, + ~trigger=`Collection(collection), + ); + }; + + switch%e (validator) { + | Ok(Some ()) + | Error () => + %expr + Update({ + ...state, + input: nextInput, + fieldsStatuses: nextFieldsStatuses^, + collectionsStatuses: [%e update_collections_statuses], + }) + | Ok(None) => + %expr + Update({ + ...state, + input: nextInput, + fieldsStatuses: nextFieldsStatuses^, + }) + }; + } + }, + ), + Exp.case( + remove_action_pat, + switch (deps) { + | [] => + %expr + { + let nextInput = [%e remove_entry_from_input_exp]; + let nextFieldsStatuses = [%e + remove_entry_from_fields_statuses_exp + ]; + switch%e (validator) { + | Ok(Some ()) + | Error () => + %expr + Update({ + ...state, + input: nextInput, + fieldsStatuses: nextFieldsStatuses, + collectionsStatuses: [%e update_collections_statuses], + }) + | Ok(None) => + %expr + Update({ + ...state, + input: nextInput, + fieldsStatuses: nextFieldsStatuses, + }) + }; + } + | [dep, ...deps] => + %expr + { + let nextInput = [%e remove_entry_from_input_exp]; + let nextFieldsStatuses = + ref([%e remove_entry_from_fields_statuses_exp]); + + %e + { + scheme + |> Form_UseFormFn_DependentFields.ast( + ~loc, + ~dep, + ~deps, + ~trigger=`Collection(collection), + ); + }; + + switch%e (validator) { + | Ok(Some ()) + | Error () => + %expr + Update({ + ...state, + input: nextInput, + fieldsStatuses: nextFieldsStatuses^, + collectionsStatuses: [%e update_collections_statuses], + }) + | Ok(None) => + %expr + Update({ + ...state, + input: nextInput, + fieldsStatuses: nextFieldsStatuses^, + }) + }; + } + }, + ), + ...acc, + ]; + }, + [], + ); +}; diff --git a/lib/ppx/Form_UseFormFn_DependentFields.re b/lib/ppx/Form_UseFormFn_DependentFields.re new file mode 100644 index 00000000..07292e41 --- /dev/null +++ b/lib/ppx/Form_UseFormFn_DependentFields.re @@ -0,0 +1,231 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = + ( + ~loc, + ~dep: FieldDep.t, + ~deps: list(FieldDep.t), + ~trigger: [ + | `Field(string) + | `Collection(Collection.t) + | `FieldOfCollection(Collection.t, string) + ], + scheme: Scheme.t, + ) => { + let validate_dep = (dep: FieldDep.t) => { + switch ( + scheme + |> List.fold_left( + (res, entry: Scheme.entry) => + switch (res, entry, dep) { + | (Some(_), _, _) => res + | (None, Field(field), DepField(dep)) => + field.name == dep ? Some(`DepField(field)) : None + | ( + None, + Collection({collection, fields}), + DepFieldOfCollection({ + collection: dep_collection, + field: dep_field, + }), + ) => + if (collection.plural != dep_collection.plural) { + None; + } else { + Some( + `DepFieldOfCollection(( + collection, + fields + |> List.find((field: Scheme.field) => + field.name == dep_field + ), + )), + ); + } + | (None, Collection(_), DepField(_)) + | (None, Field(_), DepFieldOfCollection(_)) => res + }, + None, + ) + ) { + | None => + failwith( + "Dep is not found in scheme. Please, file an issue with your use-case.", + ) + | Some(`DepField(field)) => + let field_status_expr = + field.name |> E.ref_field(~in_="nextFieldsStatuses", ~loc); + let validator_expr = field.name |> E.field(~in_="validators", ~loc); + let set_status_expr = + field.name + |> E.update_ref_field( + ~in_="nextFieldsStatuses", + ~with_=[%expr status], + ~loc, + ); + + switch (field.validator) { + | SyncValidator(Ok(Required | Optional(Some(_))) | Error ()) => + switch%expr ( + validateDependentFieldOnChange( + ~input=nextInput, + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ) + ) { + | Some(result) => nextFieldsStatuses := result + | None => () + } + | SyncValidator(Ok(Optional(None))) => + %expr + () + // Should we trigger async validator of dependency? + | AsyncValidator({mode: OnChange | OnBlur}) => + switch%expr ( + Async.validateDependentFieldOnChange( + ~input=nextInput, + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ) + ) { + | Some(result) => nextFieldsStatuses := result + | None => () + } + }; + | Some(`DepFieldOfCollection(collection, field)) => + let collection_statuses_expr = + collection.plural |> E.ref_field(~in_="nextFieldsStatuses", ~loc); + let field_status_expr = field.name |> E.field(~in_="item", ~loc); + let validator_expr = + field.name + |> E.field_of_collection_validator( + ~validators="validators", + ~collection, + ~loc, + ); + let set_status_expr = + field.name + |> E.update_ref_field_of_collection( + ~in_="nextFieldsStatuses", + ~collection, + ~with_=[%expr status], + ~index_token="index'", + ~loc, + ); + + switch (trigger) { + | `FieldOfCollection(collection', field') + when collection.plural == collection'.plural && field.name == field' => + switch (field.validator) { + | SyncValidator(Ok(Required | Optional(Some(_))) | Error ()) => + %expr + { + Belt.Array.forEachWithIndex( + [%e collection_statuses_expr], (index', item) => + if (index != index') { + switch ( + validateDependentFieldOfCollectionOnChange( + ~input=nextInput, + ~index=index', + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ) + ) { + | Some(result) => nextFieldsStatuses := result + | None => () + }; + } else { + (); + } + ); + } + + | SyncValidator(Ok(Optional(None))) => + %expr + () + // Should we trigger async validator of dependency? + | AsyncValidator({mode: OnChange | OnBlur}) => + %expr + { + Belt.Array.forEachWithIndex( + [%e collection_statuses_expr], (index', item) => + if (index != index') { + switch ( + Async.validateDependentFieldOfCollectionOnChange( + ~input=nextInput, + ~index=index', + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ) + ) { + | Some(result) => nextFieldsStatuses := result + | None => () + }; + } else { + (); + } + ); + } + } + | `Field(_) + | `Collection(_) + | `FieldOfCollection(_, _) => + switch (field.validator) { + | SyncValidator(Ok(Required | Optional(Some(_))) | Error ()) => + %expr + { + Belt.Array.forEachWithIndex( + [%e collection_statuses_expr], (index', item) => + switch ( + validateDependentFieldOfCollectionOnChange( + ~input=nextInput, + ~index=index', + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ) + ) { + | Some(result) => nextFieldsStatuses := result + | None => () + } + ); + } + | SyncValidator(Ok(Optional(None))) => + %expr + () + // Should we trigger async validator of dependency? + | AsyncValidator({mode: OnChange | OnBlur}) => + %expr + { + Belt.Array.forEachWithIndex( + [%e collection_statuses_expr], (index', item) => + switch ( + Async.validateDependentFieldOfCollectionOnChange( + ~input=nextInput, + ~index=index', + ~fieldStatus=[%e field_status_expr], + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ) + ) { + | Some(result) => nextFieldsStatuses := result + | None => () + } + ); + } + } + }; + }; + }; + + deps |> E.seq(~exp=dep |> validate_dep, ~make=validate_dep); +}; diff --git a/lib/ppx/Form_UseFormFn_Interface.re b/lib/ppx/Form_UseFormFn_Interface.re new file mode 100644 index 00000000..5fbad853 --- /dev/null +++ b/lib/ppx/Form_UseFormFn_Interface.re @@ -0,0 +1,459 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let dirty_collection_guard = (~loc, {collection, fields}: Scheme.collection) => [%expr + Belt.Array.every( + [%e Exp.ident(Lident(collection.plural) |> lid(~loc))], item => { + %e + Exp.match( + [%expr item], + [ + Exp.case( + Pat.record( + fields + |> List.map((field: Scheme.field) => + (Lident(field.name) |> lid(~loc), [%pat? Pristine]) + ), + Closed, + ), + [%expr true], + ), + Exp.case( + Pat.record( + fields + |> List.map((field: Scheme.field) => + ( + Lident(field.name) |> lid(~loc), + switch (fields, field.validator) { + | ([_x], SyncValidator(_)) => [%pat? Dirty(_)] + | ([_x], AsyncValidator(_)) => [%pat? + Dirty(_) | Validating(_) + ] + | (_, SyncValidator(_)) => [%pat? Pristine | Dirty(_)] + | (_, AsyncValidator(_)) => [%pat? + Pristine | Dirty(_) | Validating(_) + ] + }, + ) + ), + Closed, + ), + [%expr false], + ), + ], + ) + }) +]; + +let ast = (~loc, ~async: bool, scheme: Scheme.t) => { + let collections = scheme |> Scheme.collections; + + let base = [ + ("input", [%expr state.input]), + ("status", [%expr state.formStatus]), + ( + "dirty", + [%expr + () => [%e + Exp.match( + [%expr state.fieldsStatuses], + [ + Exp.case( + Pat.record( + scheme + |> List.map((entry: Scheme.entry) => + switch (entry) { + | Field(field) => ( + Lident(field.name) |> lid(~loc), + [%pat? Pristine], + ) + | Collection({collection}) => ( + Lident(collection.plural) |> lid(~loc), + Pat.var(collection.plural |> str(~loc)), + ) + } + ), + Closed, + ), + ~guard=?{ + switch (collections) { + | [] => None + | [collection] => + Some(dirty_collection_guard(~loc, collection)) + | [collection, ...collections] => + Some( + collections + |> E.conj( + ~loc, + ~exp=dirty_collection_guard(~loc, collection), + ~make=dirty_collection_guard, + ), + ) + }; + }, + [%expr false], + ), + Exp.case( + Pat.record( + scheme + |> List.map((entry: Scheme.entry) => + switch (entry) { + | Field(field) => ( + Lident(field.name) |> lid(~loc), + switch (scheme, field.validator) { + | ([_x], SyncValidator(_)) => [%pat? Dirty(_)] + | ([_x], AsyncValidator(_)) => [%pat? + Dirty(_) | Validating(_) + ] + | (_, SyncValidator(_)) => [%pat? + Pristine | Dirty(_) + ] + | (_, AsyncValidator(_)) => [%pat? + Pristine | Dirty(_) | Validating(_) + ] + }, + ) + | Collection({collection}) => ( + Lident(collection.plural) |> lid(~loc), + [%pat? _], + ) + } + ), + Closed, + ), + [%expr true], + ), + ], + ) + ] + ], + ), + ( + "valid", + if (async) { + %expr + () => + switch ( + state.input + ->validateForm(~validators, ~fieldsStatuses=state.fieldsStatuses) + ) { + | Validating(_) => None + | Valid(_) => Some(true) + | Invalid(_) => Some(false) + }; + } else { + %expr + () => + switch ( + state.input + ->validateForm(~validators, ~fieldsStatuses=state.fieldsStatuses) + ) { + | Valid(_) => true + | Invalid(_) => false + }; + }, + ), + ( + "submitting", + switch%expr (state.formStatus) { + | Submitting(_) => true + | Editing + | Submitted + | SubmissionFailed(_) => false + }, + ), + ("submit", [%expr () => Submit->dispatch]), + ("mapSubmissionError", [%expr map => MapSubmissionError(map)->dispatch]), + ( + "dismissSubmissionError", + [%expr () => DismissSubmissionError->dispatch], + ), + ( + "dismissSubmissionResult", + [%expr () => DismissSubmissionResult->dispatch], + ), + ("reset", [%expr () => Reset->dispatch]), + ]; + + let update_fns = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(field) => [ + ( + FieldPrinter.update_fn(~field=field.name), + [%expr + ( + nextInputFn => + [%e + Exp.construct( + Lident( + FieldPrinter.update_action(~field=field.name), + ) + |> lid(~loc), + Some([%expr nextInputFn]), + ) + ] + ->dispatch + ) + ], + ), + ...acc, + ] + | Collection({collection, fields}) => + fields + |> List.fold_left( + (acc, field: Scheme.field) => + [ + ( + FieldOfCollectionPrinter.update_fn( + ~collection, + ~field=field.name, + ), + [%expr + (nextInputFn, ~at as index) => + [%e + Exp.construct( + Lident( + FieldOfCollectionPrinter.update_action( + ~collection, + ~field=field.name, + ), + ) + |> lid(~loc), + Some([%expr (nextInputFn, index)]), + ) + ] + ->dispatch + ], + ), + ...acc, + ], + acc, + ) + }, + [], + ); + + let blur_fns = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(field) => [ + ( + FieldPrinter.blur_fn(~field=field.name), + [%expr + ( + () => + [%e + Exp.construct( + Lident( + FieldPrinter.blur_action(~field=field.name), + ) + |> lid(~loc), + None, + ) + ] + ->dispatch + ) + ], + ), + ...acc, + ] + | Collection({collection, fields}) => + fields + |> List.fold_left( + (acc, field: Scheme.field) => + [ + ( + FieldOfCollectionPrinter.blur_fn( + ~collection, + ~field=field.name, + ), + [%expr + (~at as index) => + [%e + Exp.construct( + Lident( + FieldOfCollectionPrinter.blur_action( + ~collection, + ~field=field.name, + ), + ) + |> lid(~loc), + Some([%expr index]), + ) + ] + ->dispatch + ], + ), + ...acc, + ], + acc, + ) + }, + [], + ); + + let result_entries = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(field) => [ + ( + FieldPrinter.result_value(~field=field.name), + switch (field.validator) { + | SyncValidator(_) => + %expr + exposeFieldResult( + [%e + field.name + |> E.field2(~in_=("state", "fieldsStatuses"), ~loc) + ], + ) + | AsyncValidator(_) => + %expr + Async.exposeFieldResult( + [%e + field.name + |> E.field2(~in_=("state", "fieldsStatuses"), ~loc) + ], + ) + }, + ), + ...acc, + ] + | Collection({collection, fields}) => + fields + |> List.fold_left( + (acc, field: Scheme.field) => + [ + ( + FieldOfCollectionPrinter.result_fn( + ~collection, + ~field=field.name, + ), + switch (field.validator) { + | SyncValidator(_) => + %expr + ( + (~at as index) => { + exposeFieldResult( + [%e + field.name + |> E.field_of_collection2( + ~in_=("state", "fieldsStatuses"), + ~collection, + ~loc, + ) + ], + ); + } + ) + | AsyncValidator(_) => + %expr + ( + (~at as index) => { + Async.exposeFieldResult( + [%e + field.name + |> E.field_of_collection2( + ~in_=("state", "fieldsStatuses"), + ~collection, + ~loc, + ) + ], + ); + } + ) + }, + ), + ...acc, + ], + acc, + ) + }, + [], + ); + + let collection_entries = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(_) => acc + | Collection({collection, validator}) => + let add_fn = ( + collection |> CollectionPrinter.add_fn, + [%expr + ( + entry => + [%e + Exp.construct( + Lident(collection |> CollectionPrinter.add_action) + |> lid(~loc), + Some([%expr entry]), + ) + ] + ->dispatch + ) + ], + ); + let remove_fn = ( + collection |> CollectionPrinter.remove_fn, + [%expr + ( + (~at as index) => + [%e + Exp.construct( + Lident(collection |> CollectionPrinter.remove_action) + |> lid(~loc), + Some([%expr index]), + ) + ] + ->dispatch + ) + ], + ); + let result_value = + switch (validator) { + | Ok(Some ()) + | Error () => + Some(( + collection |> CollectionPrinter.result_value, + collection.plural + |> E.field2(~in_=("state", "collectionsStatuses"), ~loc), + )) + | Ok(None) => None + }; + + switch (result_value) { + | Some(result_value) => [ + add_fn, + remove_fn, + result_value, + ...acc, + ] + | None => [add_fn, remove_fn, ...acc] + }; + }, + [], + ); + + E.record( + ~loc, + base + |> List.append(collection_entries) + |> List.append(result_entries) + |> List.append(blur_fns) + |> List.append(update_fns), + ); +}; diff --git a/lib/ppx/Form_UseFormFn_RestActions.re b/lib/ppx/Form_UseFormFn_RestActions.re new file mode 100644 index 00000000..7d643f65 --- /dev/null +++ b/lib/ppx/Form_UseFormFn_RestActions.re @@ -0,0 +1,165 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = (~loc, ~async) => [ + if (async) { + Exp.case( + [%pat? Submit], + switch%expr (state.formStatus) { + | Submitting(_) => NoUpdate + | Editing + | Submitted + | SubmissionFailed(_) => + switch ( + state.input + ->validateForm(~validators, ~fieldsStatuses=state.fieldsStatuses) + ) { + | Validating({fieldsStatuses, collectionsStatuses}) => + Update({...state, fieldsStatuses, collectionsStatuses}) + | Valid({output, fieldsStatuses, collectionsStatuses}) => + UpdateWithSideEffects( + { + ...state, + fieldsStatuses, + collectionsStatuses, + formStatus: + Submitting( + switch (state.formStatus) { + | SubmissionFailed(error) => Some(error) + | Editing + | Submitted + | Submitting(_) => None + }, + ), + submissionStatus: AttemptedToSubmit, + }, + ({state: _, dispatch}) => + output->onSubmit({ + notifyOnSuccess: input => SetSubmittedStatus(input)->dispatch, + notifyOnFailure: error => + SetSubmissionFailedStatus(error)->dispatch, + reset: () => Reset->dispatch, + dismissSubmissionResult: () => + DismissSubmissionResult->dispatch, + }), + ) + | Invalid({fieldsStatuses, collectionsStatuses}) => + Update({ + ...state, + fieldsStatuses, + collectionsStatuses, + formStatus: Editing, + submissionStatus: AttemptedToSubmit, + }) + } + }, + ); + } else { + Exp.case( + [%pat? Submit], + switch%expr (state.formStatus) { + | Submitting(_) => NoUpdate + | Editing + | Submitted + | SubmissionFailed(_) => + switch ( + state.input + ->validateForm(~validators, ~fieldsStatuses=state.fieldsStatuses) + ) { + | Valid({output, fieldsStatuses, collectionsStatuses}) => + UpdateWithSideEffects( + { + ...state, + fieldsStatuses, + collectionsStatuses, + formStatus: + Submitting( + switch (state.formStatus) { + | SubmissionFailed(error) => Some(error) + | Editing + | Submitted + | Submitting(_) => None + }, + ), + submissionStatus: AttemptedToSubmit, + }, + ({state: _, dispatch}) => + output->onSubmit({ + notifyOnSuccess: input => SetSubmittedStatus(input)->dispatch, + notifyOnFailure: error => + SetSubmissionFailedStatus(error)->dispatch, + reset: () => Reset->dispatch, + dismissSubmissionResult: () => + DismissSubmissionResult->dispatch, + }), + ) + | Invalid({fieldsStatuses, collectionsStatuses}) => + Update({ + ...state, + fieldsStatuses, + collectionsStatuses, + formStatus: Editing, + submissionStatus: AttemptedToSubmit, + }) + } + }, + ); + }, + Exp.case( + [%pat? SetSubmittedStatus(input)], + switch%expr (input) { + | Some(input) => + Update({ + ...state, + input, + formStatus: Submitted, + fieldsStatuses: input->initialFieldsStatuses, + }) + | None => + Update({ + ...state, + formStatus: Submitted, + fieldsStatuses: state.input->initialFieldsStatuses, + }) + }, + ), + Exp.case( + [%pat? SetSubmissionFailedStatus(error)], + [%expr Update({...state, formStatus: SubmissionFailed(error)})], + ), + Exp.case( + [%pat? MapSubmissionError(map)], + switch%expr (state.formStatus) { + | Submitting(Some(error)) => + Update({...state, formStatus: Submitting(Some(error->map))}) + | SubmissionFailed(error) => + Update({...state, formStatus: SubmissionFailed(error->map)}) + | Editing + | Submitting(None) + | Submitted => NoUpdate + }, + ), + Exp.case( + [%pat? DismissSubmissionError], + switch%expr (state.formStatus) { + | Editing + | Submitting(_) + | Submitted => NoUpdate + | SubmissionFailed(_) => Update({...state, formStatus: Editing}) + }, + ), + Exp.case( + [%pat? DismissSubmissionResult], + switch%expr (state.formStatus) { + | Editing + | Submitting(_) => NoUpdate + | Submitted + | SubmissionFailed(_) => Update({...state, formStatus: Editing}) + }, + ), + Exp.case([%pat? Reset], [%expr Update(initialInput->initialState)]), +]; diff --git a/lib/ppx/Form_UseFormFn_UpdateActions.re b/lib/ppx/Form_UseFormFn_UpdateActions.re new file mode 100644 index 00000000..03e95dcb --- /dev/null +++ b/lib/ppx/Form_UseFormFn_UpdateActions.re @@ -0,0 +1,322 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let ast = (~loc, scheme: Scheme.t) => + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(field) => [ + Exp.case( + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident(FieldPrinter.update_action(~field=field.name)) + |> lid(~loc), + Some(Pat.tuple([Pat.var("nextInputFn" |> str(~loc))])), + ), + switch (field.deps) { + | [] => + let field_status_expr = + field.name + |> E.field2(~in_=("state", "fieldsStatuses"), ~loc); + let field_input_expr = + field.name |> E.field(~in_="nextInput", ~loc); + let validator_expr = + field.name |> E.field(~in_="validators", ~loc); + let set_status_expr = + field.name + |> E.update_field2( + ~in_=("state", "fieldsStatuses"), + ~with_=[%expr status], + ~loc, + ); + + %expr + { + let nextInput = nextInputFn(state.input); + + switch%e (field.validator) { + | SyncValidator(validator) => + Form_UseFormFn_UpdateActions_SyncField.ast( + ~loc, + ~validator, + ~field_status_expr, + ~field_input_expr, + ~validator_expr, + ~set_status_expr, + ) + | AsyncValidator({mode: OnBlur, optionality}) => + Form_UseFormFn_UpdateActions_AsyncFieldInOnBlurMode.ast( + ~loc, + ~optionality, + ~field_status_expr, + ~validator_expr, + ~set_status_expr, + ) + | AsyncValidator({mode: OnChange, optionality}) => + Form_UseFormFn_UpdateActions_AsyncFieldInOnChangeMode.ast( + ~loc, + ~field, + ~optionality, + ~field_status_expr, + ~validator_expr, + ~set_status_expr, + ) + }; + }; + + | [dep, ...deps] => + %expr + { + let nextInput = nextInputFn(state.input); + let nextFieldsStatuses = ref(state.fieldsStatuses); + + %e + { + scheme + |> Form_UseFormFn_DependentFields.ast( + ~loc, + ~dep, + ~deps, + ~trigger=`Field(field.name), + ); + }; + + %e + { + let field_status_expr = + field.name + |> E.ref_field(~in_="nextFieldsStatuses", ~loc); + let field_input_expr = + field.name |> E.field(~in_="nextInput", ~loc); + let validator_expr = + field.name |> E.field(~in_="validators", ~loc); + let set_status_expr = + field.name + |> E.update_ref_field( + ~in_="nextFieldsStatuses", + ~with_=[%expr status], + ~loc, + ); + + switch (field.validator) { + | SyncValidator(validator) => + Form_UseFormFn_UpdateActions_SyncField.ast( + ~loc, + ~validator, + ~field_status_expr, + ~field_input_expr, + ~validator_expr, + ~set_status_expr, + ) + | AsyncValidator({mode: OnBlur, optionality}) => + Form_UseFormFn_UpdateActions_AsyncFieldInOnBlurMode.ast( + ~loc, + ~optionality, + ~field_status_expr, + ~validator_expr, + ~set_status_expr, + ) + | AsyncValidator({mode: OnChange, optionality}) => + Form_UseFormFn_UpdateActions_AsyncFieldInOnChangeMode.ast( + ~loc, + ~field, + ~optionality, + ~field_status_expr, + ~validator_expr, + ~set_status_expr, + ) + }; + }; + } + }, + ), + ...acc, + ] + | Collection({collection, fields}) => + fields + |> List.fold_left( + (acc, field: Scheme.field) => + [ + Exp.case( + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident( + FieldOfCollectionPrinter.update_action( + ~collection, + ~field=field.name, + ), + ) + |> lid(~loc), + Some( + Pat.tuple([ + Pat.var("nextInputFn" |> str(~loc)), + Pat.var("index" |> str(~loc)), + ]), + ), + ), + switch (field.deps) { + | [] => + let field_status_expr = + field.name + |> E.field_of_collection2( + ~in_=("state", "fieldsStatuses"), + ~collection, + ~loc, + ); + let field_input_expr = + field.name + |> E.field_of_collection( + ~in_="nextInput", + ~collection, + ~loc, + ); + let validator_expr = + field.name + |> E.field_of_collection_validator( + ~validators="validators", + ~collection, + ~loc, + ); + let set_status_expr = + field.name + |> E.update_field_of_collection2( + ~in_=("state", "fieldsStatuses"), + ~collection, + ~with_=[%expr status], + ~loc, + ); + + %expr + { + let nextInput = nextInputFn(state.input); + + switch%e (field.validator) { + | SyncValidator(validator) => + Form_UseFormFn_UpdateActions_SyncFieldOfCollection.ast( + ~loc, + ~validator, + ~field_status_expr, + ~field_input_expr, + ~validator_expr, + ~set_status_expr, + ) + | AsyncValidator({mode: OnBlur, optionality}) => + Form_UseFormFn_UpdateActions_AsyncFieldOfCollectionInOnBlurMode.ast( + ~loc, + ~optionality, + ~field_status_expr, + ~validator_expr, + ~set_status_expr, + ) + | AsyncValidator({mode: OnChange, optionality}) => + Form_UseFormFn_UpdateActions_AsyncFieldOfCollectionInOnChangeMode.ast( + ~loc, + ~field, + ~collection, + ~optionality, + ~field_status_expr, + ~validator_expr, + ~set_status_expr, + ) + }; + }; + + | [dep, ...deps] => + %expr + { + let nextInput = nextInputFn(state.input); + let nextFieldsStatuses = ref(state.fieldsStatuses); + + %e + { + scheme + |> Form_UseFormFn_DependentFields.ast( + ~loc, + ~dep, + ~deps, + ~trigger= + `FieldOfCollection(( + collection, + field.name, + )), + ); + }; + + %e + { + let field_status_expr = + field.name + |> E.ref_field_of_collection( + ~in_="nextFieldsStatuses", + ~collection, + ~loc, + ); + let field_input_expr = + field.name + |> E.field_of_collection( + ~in_="nextInput", + ~collection, + ~loc, + ); + let validator_expr = + field.name + |> E.field_of_collection_validator( + ~validators="validators", + ~collection, + ~loc, + ); + let set_status_expr = + field.name + |> E.update_ref_field_of_collection( + ~in_="nextFieldsStatuses", + ~collection, + ~with_=[%expr status], + ~loc, + ); + + switch (field.validator) { + | SyncValidator(validator) => + Form_UseFormFn_UpdateActions_SyncFieldOfCollection.ast( + ~loc, + ~validator, + ~field_status_expr, + ~field_input_expr, + ~validator_expr, + ~set_status_expr, + ) + | AsyncValidator({mode: OnBlur, optionality}) => + Form_UseFormFn_UpdateActions_AsyncFieldOfCollectionInOnBlurMode.ast( + ~loc, + ~optionality, + ~field_status_expr, + ~validator_expr, + ~set_status_expr, + ) + | AsyncValidator({mode: OnChange, optionality}) => + Form_UseFormFn_UpdateActions_AsyncFieldOfCollectionInOnChangeMode.ast( + ~loc, + ~field, + ~collection, + ~optionality, + ~field_status_expr, + ~validator_expr, + ~set_status_expr, + ) + }; + }; + } + }, + ), + ...acc, + ], + acc, + ) + }, + [], + ); diff --git a/lib/ppx/Form_UseFormFn_UpdateActions_AsyncFieldInOnBlurMode.re b/lib/ppx/Form_UseFormFn_UpdateActions_AsyncFieldInOnBlurMode.re new file mode 100644 index 00000000..04ef4820 --- /dev/null +++ b/lib/ppx/Form_UseFormFn_UpdateActions_AsyncFieldInOnBlurMode.re @@ -0,0 +1,67 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = + ( + ~loc, + ~optionality: option(FieldOptionality.t), + ~field_status_expr: expression, + ~validator_expr: expression, + ~set_status_expr: expression, + ) => [%expr + Update({ + ...state, + input: nextInput, + fieldsStatuses: + switch%e (optionality) { + | None => + %expr + { + Async.validateFieldOnChangeInOnBlurMode( + ~input=nextInput, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(OptionType) => + %expr + { + Async.validateFieldOfOptionTypeOnChangeInOnBlurMode( + ~input=nextInput, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(StringType) => + %expr + { + Async.validateFieldOfStringTypeOnChangeInOnBlurMode( + ~input=nextInput, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(OptionStringType) => + %expr + { + Async.validateFieldOfOptionStringTypeOnChangeInOnBlurMode( + ~input=nextInput, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + }, + }) +]; diff --git a/lib/ppx/Form_UseFormFn_UpdateActions_AsyncFieldInOnChangeMode.re b/lib/ppx/Form_UseFormFn_UpdateActions_AsyncFieldInOnChangeMode.re new file mode 100644 index 00000000..f8c24ef7 --- /dev/null +++ b/lib/ppx/Form_UseFormFn_UpdateActions_AsyncFieldInOnChangeMode.re @@ -0,0 +1,85 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = + ( + ~loc, + ~field: Scheme.field, + ~optionality: option(FieldOptionality.t), + ~field_status_expr: expression, + ~validator_expr: expression, + ~set_status_expr: expression, + ) => { + %expr + { + let nextFieldsStatuses = + switch%e (optionality) { + | None => + %expr + { + Async.validateFieldOnChangeInOnChangeMode( + ~input=nextInput, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(OptionType) => + %expr + { + Async.validateFieldOfOptionTypeOnChangeInOnChangeMode( + ~input=nextInput, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(StringType) => + %expr + { + Async.validateFieldOfStringTypeOnChangeInOnChangeMode( + ~input=nextInput, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(OptionStringType) => + %expr + { + Async.validateFieldOfOptionStringTypeOnChangeInOnChangeMode( + ~input=nextInput, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + }; + switch ([%e field.name |> E.field(~in_="nextFieldsStatuses", ~loc)]) { + | Validating(value) => + UpdateWithSideEffects( + {...state, input: nextInput, fieldsStatuses: nextFieldsStatuses}, + ({state: _, dispatch}) => { + %e + E.apply_field2( + ~in_=("validators", field.name), + ~fn="validateAsync", + ~args=[(Nolabel, [%expr (value, dispatch)])], + ~loc, + ) + }, + ) + | Pristine + | Dirty(_, Shown | Hidden) => + Update({...state, input: nextInput, fieldsStatuses: nextFieldsStatuses}) + }; + }; +}; diff --git a/lib/ppx/Form_UseFormFn_UpdateActions_AsyncFieldOfCollectionInOnBlurMode.re b/lib/ppx/Form_UseFormFn_UpdateActions_AsyncFieldOfCollectionInOnBlurMode.re new file mode 100644 index 00000000..01e04f5b --- /dev/null +++ b/lib/ppx/Form_UseFormFn_UpdateActions_AsyncFieldOfCollectionInOnBlurMode.re @@ -0,0 +1,71 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = + ( + ~loc, + ~optionality: option(FieldOptionality.t), + ~field_status_expr: expression, + ~validator_expr: expression, + ~set_status_expr: expression, + ) => [%expr + Update({ + ...state, + input: nextInput, + fieldsStatuses: + switch%e (optionality) { + | None => + %expr + { + Async.validateFieldOfCollectionOnChangeInOnBlurMode( + ~input=nextInput, + ~index, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(OptionType) => + %expr + { + Async.validateFieldOfCollectionOfOptionTypeOnChangeInOnBlurMode( + ~input=nextInput, + ~index, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(StringType) => + %expr + { + Async.validateFieldOfCollectionOfStringTypeOnChangeInOnBlurMode( + ~input=nextInput, + ~index, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(OptionStringType) => + %expr + { + Async.validateFieldOfCollectionOfOptionStringTypeOnChangeInOnBlurMode( + ~input=nextInput, + ~index, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + }, + }) +]; diff --git a/lib/ppx/Form_UseFormFn_UpdateActions_AsyncFieldOfCollectionInOnChangeMode.re b/lib/ppx/Form_UseFormFn_UpdateActions_AsyncFieldOfCollectionInOnChangeMode.re new file mode 100644 index 00000000..ccdf487c --- /dev/null +++ b/lib/ppx/Form_UseFormFn_UpdateActions_AsyncFieldOfCollectionInOnChangeMode.re @@ -0,0 +1,95 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = + ( + ~loc, + ~field: Scheme.field, + ~collection: Collection.t, + ~optionality: option(FieldOptionality.t), + ~field_status_expr: expression, + ~validator_expr: expression, + ~set_status_expr: expression, + ) => { + %expr + { + let nextFieldsStatuses = + switch%e (optionality) { + | None => + %expr + { + Async.validateFieldOfCollectionOnChangeInOnChangeMode( + ~input=nextInput, + ~index, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(OptionType) => + %expr + { + Async.validateFieldOfCollectionOfOptionTypeOnChangeInOnChangeMode( + ~input=nextInput, + ~index, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(StringType) => + %expr + { + Async.validateFieldOfCollectionOfStringTypeOnChangeInOnChangeMode( + ~input=nextInput, + ~index, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Some(OptionStringType) => + %expr + { + Async.validateFieldOfCollectionOfOptionStringTypeOnChangeInOnChangeMode( + ~input=nextInput, + ~index, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + }; + switch ( + [%e + field.name + |> E.field_of_collection(~in_="nextFieldsStatuses", ~collection, ~loc) + ] + ) { + | Validating(value) => + UpdateWithSideEffects( + {...state, input: nextInput, fieldsStatuses: nextFieldsStatuses}, + ({state: _, dispatch}) => { + %e + E.apply_field4( + ~in_=("validators", collection.plural, "fields", field.name), + ~fn="validateAsync", + ~args=[(Nolabel, [%expr (value, index, dispatch)])], + ~loc, + ) + }, + ) + | Pristine + | Dirty(_, Shown | Hidden) => + Update({...state, input: nextInput, fieldsStatuses: nextFieldsStatuses}) + }; + }; +}; diff --git a/lib/ppx/Form_UseFormFn_UpdateActions_SyncField.re b/lib/ppx/Form_UseFormFn_UpdateActions_SyncField.re new file mode 100644 index 00000000..e3a8b196 --- /dev/null +++ b/lib/ppx/Form_UseFormFn_UpdateActions_SyncField.re @@ -0,0 +1,42 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = + ( + ~loc, + ~validator: result(FieldValidator.sync, unit), + ~field_status_expr: expression, + ~field_input_expr: expression, + ~validator_expr: expression, + ~set_status_expr: expression, + ) => [%expr + Update({ + ...state, + input: nextInput, + fieldsStatuses: + switch%e (validator) { + | Ok(Required | Optional(Some(_))) + | Error () => + %expr + { + validateFieldOnChangeWithValidator( + ~input=nextInput, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Ok(Optional(None)) => + %expr + validateFieldOnChangeWithoutValidator( + ~fieldInput=[%e field_input_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ) + }, + }) +]; diff --git a/lib/ppx/Form_UseFormFn_UpdateActions_SyncFieldOfCollection.re b/lib/ppx/Form_UseFormFn_UpdateActions_SyncFieldOfCollection.re new file mode 100644 index 00000000..38713cfb --- /dev/null +++ b/lib/ppx/Form_UseFormFn_UpdateActions_SyncFieldOfCollection.re @@ -0,0 +1,43 @@ +open Meta; +open Ast; +open AstHelpers; + +open Ppxlib; +open Ast_helper; + +let ast = + ( + ~loc, + ~validator: result(FieldValidator.sync, unit), + ~field_status_expr: expression, + ~field_input_expr: expression, + ~validator_expr: expression, + ~set_status_expr: expression, + ) => [%expr + Update({ + ...state, + input: nextInput, + fieldsStatuses: + switch%e (validator) { + | Ok(Required | Optional(Some(_))) + | Error () => + %expr + { + validateFieldOfCollectionOnChangeWithValidator( + ~input=nextInput, + ~index, + ~fieldStatus=[%e field_status_expr], + ~submissionStatus=state.submissionStatus, + ~validator=[%e validator_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ); + } + | Ok(Optional(None)) => + %expr + validateFieldOnChangeWithoutValidator( + ~fieldInput=[%e field_input_expr], + ~setStatus=[%e [%expr status => [%e set_status_expr]]], + ) + }, + }) +]; diff --git a/lib/ppx/Form_ValidateFormFn.re b/lib/ppx/Form_ValidateFormFn.re new file mode 100644 index 00000000..127230d6 --- /dev/null +++ b/lib/ppx/Form_ValidateFormFn.re @@ -0,0 +1,1552 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let field_result_var = (~field: string) => field ++ "Result"; +let field_result_visibility_var = (~field: string) => + field ++ "ResultVisibility"; +let fields_of_collection_result_var = (collection: Collection.t) => + collection.plural ++ "CollectionFieldsResult"; +let whole_collection_result_var = (collection: Collection.t) => + collection.plural ++ "CollectionResult"; +let collection_fields_statuses_var = (collection: Collection.t) => + collection.plural ++ "CollectionFieldsStatuses"; + +let validate_field_without_validator = (~field: Scheme.field, ~loc) => [%expr + (Ok([%e field.name |> E.field(~in_="input", ~loc)]), Hidden) +]; + +let validate_field_of_collection_without_validator = + (~collection: Collection.t, ~field: Scheme.field, ~loc) => [%expr + ( + Ok( + [%e + Exp.field( + [%expr + Array.getUnsafe( + [%e collection.plural |> E.field(~in_="input", ~loc)], + index, + ) + ], + Lident(field.name) |> lid(~loc), + ) + ], + ), + Hidden, + ) +]; + +let validate_field_with_sync_validator = (~field: Scheme.field, ~loc) => [%expr + ( + switch ([%e field.name |> E.field(~in_="fieldsStatuses", ~loc)]) { + | Pristine => + %e + E.apply_field2( + ~in_=("validators", field.name), + ~fn="validate", + ~args=[(Nolabel, [%expr input])], + ~loc, + ) + | Dirty(result, _) => result + }, + Shown, + ) +]; + +let validate_field_of_collection_with_sync_validator = + (~field: Scheme.field, ~collection: Collection.t, ~loc) => [%expr + ( + switch ([%e field.name |> E.field(~in_="fieldStatus", ~loc)]) { + | Pristine => + %e + E.apply_field4( + ~in_=("validators", collection.plural, "fields", field.name), + ~fn="validate", + ~args=[(Nolabel, [%expr input]), (Labelled("at"), [%expr index])], + ~loc, + ) + | Dirty(result, _) => result + }, + Shown, + ) +]; + +let validate_field_with_async_validator = (~field: Scheme.field, ~loc) => [%expr + ( + switch ([%e field.name |> E.field(~in_="fieldsStatuses", ~loc)]) { + | Validating(value) => `Validating(value) + | Pristine => + // If field is not touched, it either "empty" or has initial input + // If async field optional, then empty state is valid + // If it has initial value, in general it's from a server, hence valid + // If it's not from server and sync validator returned OK() but value is invalid, + // it should be rejected by the server on submit anyway + // So it doesn't worth to do 2+ requests on submission + `Result( + [%e + E.apply_field2( + ~in_=("validators", field.name), + ~fn="validate", + ~args=[(Nolabel, [%expr input])], + ~loc, + ) + ], + ) + | Dirty(result, _) => + // This field was updated by user so all its validators already run + `Result(result) + }, + Shown, + ) +]; + +let validate_field_of_collection_with_async_validator = + (~field: Scheme.field, ~collection: Collection.t, ~loc) => [%expr + ( + switch ([%e field.name |> E.field(~in_="fieldStatus", ~loc)]) { + | Validating(value) => `Validating(value) + | Dirty(result, _) => `Result(result) + | Pristine => + `Result( + [%e + E.apply_field4( + ~in_=("validators", collection.plural, "fields", field.name), + ~fn="validate", + ~args=[ + (Nolabel, [%expr input]), + (Labelled("at"), [%expr index]), + ], + ~loc, + ) + ], + ) + }, + Shown, + ) +]; + +let validate_whole_collection = (~collection: Collection.t, ~loc) => + E.apply_field2( + ~in_=("validators", collection.plural), + ~fn="collection", + ~args=[(Nolabel, [%expr input])], + ~loc, + ); + +let ok_pat_for_sync_field = (~loc, field: Scheme.field) => + Pat.tuple([ + Pat.alias( + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident("Ok") |> lid(~loc), + Some(Pat.tuple([Pat.var(field.name |> str(~loc))])), + ), + field_result_var(~field=field.name) |> str(~loc), + ), + Pat.var(field_result_visibility_var(~field=field.name) |> str(~loc)), + ]); + +let ok_pat_for_async_field = (~loc, field: Scheme.field) => + Pat.tuple([ + Pat.variant( + "Result", + Some( + Pat.alias( + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident("Ok") |> lid(~loc), + Some(Pat.tuple([Pat.var(field.name |> str(~loc))])), + ), + field_result_var(~field=field.name) |> str(~loc), + ), + ), + ), + Pat.var(field_result_visibility_var(~field=field.name) |> str(~loc)), + ]); + +let ok_pat_for_fields_of_async_collection = (~loc, collection: Collection.t) => + Pat.variant( + "FieldsOfCollectionResult", + Some( + Pat.tuple([ + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident("Ok") |> lid(~loc), + Some(Pat.tuple([Pat.var(collection.plural |> str(~loc))])), + ), + Pat.var(collection |> collection_fields_statuses_var |> str(~loc)), + ]), + ), + ); + +let ok_pat_for_collection = (~loc, collection: Collection.t) => + Pat.alias( + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident("Ok") |> lid(~loc), + Some([%pat? ()]), + ), + collection |> whole_collection_result_var |> str(~loc), + ); + +let ok_pat_for_fields_of_collection = (~loc, collection: Collection.t) => + Pat.tuple([ + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident("Ok") |> lid(~loc), + Some(Pat.tuple([Pat.var(collection.plural |> str(~loc))])), + ), + Pat.var(collection |> collection_fields_statuses_var |> str(~loc)), + ]); + +let result_pat_for_collection = (~loc, collection: Collection.t) => + Pat.var(collection |> whole_collection_result_var |> str(~loc)); + +let error_pat_for_sync_field_in_single_field_form = + (~loc, field: Scheme.field) => + Pat.tuple([ + Pat.alias( + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident("Error") |> lid(~loc), + Some([%pat? _]), + ), + field_result_var(~field=field.name) |> str(~loc), + ), + Pat.var(field_result_visibility_var(~field=field.name) |> str(~loc)), + ]); + +let error_pat_for_async_field_in_single_field_form = + (~loc, field: Scheme.field) => + Pat.tuple([ + Pat.variant( + "Result", + Some( + Pat.alias( + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident("Error") |> lid(~loc), + Some([%pat? _]), + ), + field_result_var(~field=field.name) |> str(~loc), + ), + ), + ), + Pat.var(field_result_visibility_var(~field=field.name) |> str(~loc)), + ]); + +let error_pat_for_sync_field_in_multi_field_form = (~loc, field: Scheme.field) => + Pat.tuple([ + Pat.or_( + Pat.alias( + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident("Ok") |> lid(~loc), + Some([%pat? _]), + ), + field_result_var(~field=field.name) |> str(~loc), + ), + Pat.alias( + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident("Error") |> lid(~loc), + Some([%pat? _]), + ), + field_result_var(~field=field.name) |> str(~loc), + ), + ), + Pat.var(field_result_visibility_var(~field=field.name) |> str(~loc)), + ]); + +let error_pat_for_async_field_in_multi_field_form = + (~loc, field: Scheme.field) => + Pat.tuple([ + Pat.variant( + "Result", + Some( + Pat.or_( + Pat.alias( + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident("Ok") |> lid(~loc), + Some([%pat? _]), + ), + field_result_var(~field=field.name) |> str(~loc), + ), + Pat.alias( + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident("Error") |> lid(~loc), + Some([%pat? _]), + ), + field_result_var(~field=field.name) |> str(~loc), + ), + ), + ), + ), + Pat.var(field_result_visibility_var(~field=field.name) |> str(~loc)), + ]); + +let error_pat_for_fields_of_collection_in_single_field_form_without_collection_validator = + (~loc, collection: Collection.t) => + Pat.tuple([ + [%pat? Error(_)], + Pat.var(collection |> collection_fields_statuses_var |> str(~loc)), + ]); + +let error_pat_for_fields_of_collection_in_multi_field_form_or_single_field_form_with_collection_validator = + (~loc, collection: Collection.t) => + Pat.tuple([ + [%pat? Ok(_) | Error(_)], + Pat.var(collection |> collection_fields_statuses_var |> str(~loc)), + ]); + +let error_pat_for_fields_of_collection_in_single_field_async_form_without_collection_validator = + (~loc, collection: Collection.t) => + Pat.variant( + "FieldsOfCollectionResult", + Some( + Pat.tuple([ + [%pat? Error(_)], + Pat.var(collection |> collection_fields_statuses_var |> str(~loc)), + ]), + ), + ); + +let error_pat_for_fields_statuses_of_async_collection = + (~loc, collection: Collection.t) => + Pat.variant( + "FieldsOfCollectionResult", + Some( + Pat.tuple([ + [%pat? Ok(_) | Error(_)], + Pat.var(collection |> collection_fields_statuses_var |> str(~loc)), + ]), + ), + ); + +let result_and_visibility_pat_for_field = (~loc, field: Scheme.field) => + Pat.tuple([ + Pat.var(field_result_var(~field=field.name) |> str(~loc)), + Pat.var(field_result_visibility_var(~field=field.name) |> str(~loc)), + ]); + +let result_and_visibility_pat_for_async_field = (~loc, field: Scheme.field) => + Pat.tuple([ + Pat.variant( + "Result", + Some(Pat.var(field_result_var(~field=field.name) |> str(~loc))), + ), + Pat.var(field_result_visibility_var(~field=field.name) |> str(~loc)), + ]); + +let result_pat_for_fields_of_collection = (~loc, collection: Collection.t) => + Pat.var(collection |> fields_of_collection_result_var |> str(~loc)); + +let output_field_record_field = (~loc, field: Scheme.field) => ( + Lident(field.name) |> lid(~loc), + Exp.ident(Lident(field.name) |> lid(~loc)), +); + +let output_collection_record_field = (~loc, collection: Collection.t) => ( + Lident(collection.plural) |> lid(~loc), + Exp.ident(Lident(collection.plural) |> lid(~loc)), +); + +let field_dirty_status_record_field = (~loc, field: Scheme.field) => ( + Lident(field.name) |> lid(~loc), + [%expr + Dirty( + [%e + Exp.ident(Lident(field_result_var(~field=field.name)) |> lid(~loc)) + ], + [%e + Exp.ident( + Lident(field_result_visibility_var(~field=field.name)) + |> lid(~loc), + ) + ], + ) + ], +); + +let async_field_dirty_or_validating_status_record_field = + (~loc, field: Scheme.field) => ( + Lident(field.name) |> lid(~loc), + switch%expr ( + [%e + Exp.ident(Lident(field_result_var(~field=field.name)) |> lid(~loc)) + ] + ) { + | `Validating(value) => Validating(value) + | `Result(result) => + Dirty( + result, + [%e + Exp.ident( + Lident(field_result_visibility_var(~field=field.name)) + |> lid(~loc), + ) + ], + ) + }, +); + +let collection_that_might_be_in_validating_state_status_record_field = + (~loc, collection: Collection.t) => ( + Lident(collection.plural) |> lid(~loc), + switch%expr ( + [%e + Exp.ident( + Lident(collection |> fields_of_collection_result_var) |> lid(~loc), + ) + ] + ) { + | `ValidatingFieldsOfCollection(statuses) => statuses + | `FieldsOfCollectionResult(_, statuses) => statuses + }, +); + +let collection_statuses_record_field = (~loc, collection: Collection.t) => ( + Lident(collection.plural) |> lid(~loc), + Exp.ident( + Lident(collection |> collection_fields_statuses_var) |> lid(~loc), + ), +); + +let collections_statuses_record = + (~loc, collections: list(Scheme.collection)) => + Exp.record( + collections + |> List.map(({collection, validator}: Scheme.collection) => + ( + Lident(collection.plural) |> lid(~loc), + switch (validator) { + | Ok(Some ()) + | Error () => + %expr + Some( + [%e + Exp.ident( + Lident(collection |> whole_collection_result_var) + |> lid(~loc), + ) + ], + ) + | Ok(None) => + %expr + () + }, + ) + ), + None, + ); + +let validate_fields_of_collection_in_sync_form = + ( + ~collection: Collection.t, + ~fields: list(Scheme.field), + ~output_type: ItemType.t, + ~loc: Location.t, + ) => { + let match_values = + Exp.tuple([ + [%expr output], + ...fields + |> List.map((field: Scheme.field) => + switch (field.validator) { + | SyncValidator(Ok(Required | Optional(Some(_))) | Error ()) => + validate_field_of_collection_with_sync_validator( + ~collection, + ~field, + ~loc, + ) + | SyncValidator(Ok(Optional(None))) => + validate_field_of_collection_without_validator( + ~collection, + ~field, + ~loc, + ) + | AsyncValidator(_) => + failwith( + "Form that supposed to be without async validators has one. Please, file an issue with yoour use-case.", + ) + } + ), + ]); + + let ok_case = + Exp.case( + Pat.tuple([ + Pat.construct( + ~attrs=[explicit_arity(~loc)], + Lident("Ok") |> lid(~loc), + Some(Pat.tuple([Pat.var("output" |> str(~loc))])), + ), + ...fields |> List.map(ok_pat_for_sync_field(~loc)), + ]), + [%expr + { + ignore( + Js.Array2.push( + output, + [%e + Exp.record( + fields |> List.map(output_field_record_field(~loc)), + None, + ) + ], + ), + ); + ignore( + Js.Array2.push( + statuses, + [%e + Exp.record( + fields |> List.map(field_dirty_status_record_field(~loc)), + None, + ) + ], + ), + ); + (Ok(output), statuses); + } + ], + ); + + let error_case = + Exp.case( + Pat.tuple([ + [%pat? Ok(_) | Error(_)], + ...fields |> List.map(result_and_visibility_pat_for_field(~loc)), + ]), + [%expr + { + ignore( + Js.Array2.push( + statuses, + [%e + Exp.record( + fields |> List.map(field_dirty_status_record_field(~loc)), + None, + ) + ], + ), + ); + (Error(), statuses); + } + ], + ); + + %expr + { + Belt.Array.reduceWithIndex( + [%e collection.plural |> E.field(~in_="fieldsStatuses", ~loc)], + (Ok([||]), [||]), + ( + ( + output: result(array([%t output_type |> ItemType.unpack]), unit), + statuses: + array( + [%t + Typ.constr( + Lident(collection |> CollectionPrinter.fields_statuses_type) + |> lid(~loc), + [], + ) + ], + ), + ), + fieldStatus, + index, + ) => { + %e + Exp.match(match_values, [ok_case, error_case]) + }); + }; +}; + +let validate_fields_of_collection_in_async_form = + ( + ~collection: Collection.t, + ~fields: list(Scheme.field), + ~output_type: ItemType.t, + ~loc: Location.t, + ) => { + let fields_statuses_type = + Typ.constr( + Lident(collection |> CollectionPrinter.fields_statuses_type) + |> lid(~loc), + [], + ); + + let match_values = + Exp.tuple([ + [%expr result], + ...fields + |> List.map((field: Scheme.field) => + switch (field.validator) { + | SyncValidator(Ok(Required | Optional(Some(_))) | Error ()) => + validate_field_of_collection_with_sync_validator( + ~collection, + ~field, + ~loc, + ) + | SyncValidator(Ok(Optional(None))) => + validate_field_of_collection_without_validator( + ~collection, + ~field, + ~loc, + ) + | AsyncValidator(_) => + validate_field_of_collection_with_async_validator( + ~collection, + ~field, + ~loc, + ) + } + ), + ]); + + let validating_case = + Exp.case( + P.or_( + ~pat= + Pat.tuple([ + [%pat? `ValidatingFieldsOfCollection(statuses)], + ...fields |> List.map(result_and_visibility_pat_for_field(~loc)), + ]), + ~make= + (field: Scheme.field) => + Pat.tuple([ + [%pat? `FieldsOfCollectionResult(Ok(_) | Error(_), statuses)], + ...fields + |> List.map((field': Scheme.field) => + if (field'.name == field.name) { + Pat.tuple([ + Pat.alias( + Pat.variant("Validating", Some(Pat.any())), + field_result_var(~field=field.name) |> str(~loc), + ), + Pat.var( + field_result_visibility_var(~field=field.name) + |> str(~loc), + ), + ]); + } else { + field' |> result_and_visibility_pat_for_field(~loc); + } + ), + ]), + fields + |> List.filter((field: Scheme.field) => + switch (field.validator) { + | SyncValidator(_) => false + | AsyncValidator(_) => true + } + ), + ), + [%expr + { + ignore( + Js.Array2.push( + statuses, + [%e + Exp.record( + fields + |> List.map((field: Scheme.field) => + switch (field.validator) { + | SyncValidator(_) => + field |> field_dirty_status_record_field(~loc) + | AsyncValidator(_) => + field + |> async_field_dirty_or_validating_status_record_field( + ~loc, + ) + } + ), + None, + ) + ], + ), + ); + `ValidatingFieldsOfCollection(statuses); + } + ], + ); + + let ok_case = + Exp.case( + Pat.tuple([ + [%pat? `FieldsOfCollectionResult(Ok(output), statuses)], + ...fields + |> List.map((field: Scheme.field) => + switch (field.validator) { + | SyncValidator(_) => field |> ok_pat_for_sync_field(~loc) + | AsyncValidator(_) => field |> ok_pat_for_async_field(~loc) + } + ), + ]), + [%expr + { + ignore( + Js.Array2.push( + output, + [%e + Exp.record( + fields |> List.map(output_field_record_field(~loc)), + None, + ) + ], + ), + ); + ignore( + Js.Array2.push( + statuses, + [%e + Exp.record( + fields |> List.map(field_dirty_status_record_field(~loc)), + None, + ) + ], + ), + ); + `FieldsOfCollectionResult((Ok(output), statuses)); + } + ], + ); + + let error_case = + Exp.case( + Pat.tuple([ + [%pat? `FieldsOfCollectionResult(Ok(_) | Error(_), statuses)], + ...fields |> List.map(result_and_visibility_pat_for_field(~loc)), + ]), + [%expr + { + ignore( + Js.Array2.push( + statuses, + [%e + Exp.record( + fields + |> List.map((field: Scheme.field) => + switch (field.validator) { + | SyncValidator(_) => + field |> field_dirty_status_record_field(~loc) + | AsyncValidator(_) => + field + |> async_field_dirty_or_validating_status_record_field( + ~loc, + ) + } + ), + None, + ) + ], + ), + ); + `FieldsOfCollectionResult((Error(), statuses)); + } + ], + ); + + %expr + { + Belt.Array.reduceWithIndex( + [%e collection.plural |> E.field(~in_="fieldsStatuses", ~loc)], + `FieldsOfCollectionResult((Ok([||]), [||])), + ( + result: [ + | `ValidatingFieldsOfCollection(array([%t fields_statuses_type])) + | `FieldsOfCollectionResult( + result(array([%t output_type |> ItemType.unpack]), unit), + array([%t fields_statuses_type]), + ) + ], + fieldStatus, + index, + ) => { + %e + Exp.match(match_values, [validating_case, ok_case, error_case]) + }); + }; +}; + +module Sync = { + let ast = (~scheme: Scheme.t, ~loc) => { + let anything_validatable = + scheme + |> List.exists((entry: Scheme.entry) => + switch (entry) { + | Field({ + validator: + SyncValidator(Ok(Required | Optional(Some ())) | Error ()), + }) => + true + | Field({validator: SyncValidator(Ok(Optional(None)))}) => false + | Field({validator: AsyncValidator(_)}) => true + | Collection({validator: Ok(Some ()) | Error ()}) => true + | Collection({validator: Ok(None), fields}) => + fields + |> List.exists((field: Scheme.field) => + switch (field.validator) { + | SyncValidator( + Ok(Required | Optional(Some ())) | Error (), + ) => + true + | SyncValidator(Ok(Optional(None))) => false + | AsyncValidator(_) => true + } + ) + } + ); + + [%stri + let validateForm = [%e + Exp.fun_( + Nolabel, + None, + Pat.constraint_([%pat? input], [%type: input]), + Exp.fun_( + Labelled("validators"), + None, + Pat.constraint_( + anything_validatable ? [%pat? validators] : [%pat? _], + [%type: validators], + ), + Exp.fun_( + Labelled("fieldsStatuses"), + None, + Pat.constraint_( + anything_validatable ? [%pat? fieldsStatuses] : [%pat? _], + [%type: fieldsStatuses], + ), + [%type: + formValidationResult( + output, + fieldsStatuses, + collectionsStatuses, + ) + ] + |> Exp.constraint_( + [%expr + { + %e + { + let collections = scheme |> Scheme.collections; + + let match_values = { + let value = (entry: Scheme.entry) => + switch (entry) { + | Field( + { + validator: + SyncValidator( + Ok(Required | Optional(Some(_))) | + Error (), + ), + } as field, + ) => + validate_field_with_sync_validator( + ~field, + ~loc, + ) + | Field( + { + validator: + SyncValidator(Ok(Optional(None))), + } as field, + ) => + validate_field_without_validator(~field, ~loc) + | Field({name, validator: AsyncValidator(_)}) => + failwith( + "Form that supposed to be without async validators has one. Please, file an issue with yoour use-case.", + ) + | Collection({ + collection, + fields, + validator, + output_type, + }) => + switch (validator) { + | Ok(Some ()) + | Error () => + %expr + ( + [%e + validate_whole_collection( + ~collection, + ~loc, + ) + ], + [%e + validate_fields_of_collection_in_sync_form( + ~collection, + ~fields, + ~output_type, + ~loc, + ) + ], + ) + | Ok(None) => + validate_fields_of_collection_in_sync_form( + ~collection, + ~fields, + ~output_type, + ~loc, + ) + } + }; + switch (scheme) { + | [x] => x |> value + | _ => scheme |> List.map(value) |> Exp.tuple + }; + }; + + let ok_case = { + let pat = { + let entry = (entry: Scheme.entry) => + switch (entry) { + | Field(field) => + field |> ok_pat_for_sync_field(~loc) + | Collection({collection, validator}) => + switch (validator) { + | Ok(Some ()) + | Error () => [%pat? + ( + [%p + collection + |> ok_pat_for_collection(~loc) + ], + [%p + collection + |> ok_pat_for_fields_of_collection( + ~loc, + ) + ], + ) + ] + | Ok(None) => + collection + |> ok_pat_for_fields_of_collection(~loc) + } + }; + switch (scheme) { + | [x] => x |> entry + | _ => scheme |> List.map(entry) |> Pat.tuple + }; + }; + let expr = { + let output = + Exp.record( + scheme + |> List.map((entry: Scheme.entry) => + switch (entry) { + | Field(field) => + field + |> output_field_record_field(~loc) + | Collection({collection}) => + collection + |> output_collection_record_field( + ~loc, + ) + } + ), + None, + ); + let fields_statuses = + Exp.record( + scheme + |> List.map((entry: Scheme.entry) => + switch (entry) { + | Field(field) => + field + |> field_dirty_status_record_field( + ~loc, + ) + | Collection({collection}) => + collection + |> collection_statuses_record_field( + ~loc, + ) + } + ), + None, + ); + + switch (collections) { + | [] => + %expr + Valid({ + output: [%e output], + fieldsStatuses: [%e fields_statuses], + collectionsStatuses: (), + }) + | collections => + %expr + Valid({ + output: [%e output], + fieldsStatuses: [%e fields_statuses], + collectionsStatuses: [%e + collections + |> collections_statuses_record(~loc) + ], + }) + }; + }; + Exp.case(pat, expr); + }; + + let error_case = { + let pat = { + let entry_of_one = (entry: Scheme.entry) => + switch (entry) { + | Field(field) => + field + |> error_pat_for_sync_field_in_single_field_form( + ~loc, + ) + | Collection({collection, validator}) => + switch (validator) { + | Ok(Some ()) + | Error () => [%pat? + ( + [%p + collection + |> result_pat_for_collection(~loc) + ], + [%p + collection + |> error_pat_for_fields_of_collection_in_multi_field_form_or_single_field_form_with_collection_validator( + ~loc, + ) + ], + ) + ] + | Ok(None) => + collection + |> error_pat_for_fields_of_collection_in_single_field_form_without_collection_validator( + ~loc, + ) + } + }; + let entry_of_many = (entry: Scheme.entry) => + switch (entry) { + | Field(field) => + field + |> error_pat_for_sync_field_in_multi_field_form( + ~loc, + ) + | Collection({collection, validator}) => + switch (validator) { + | Ok(Some ()) + | Error () => [%pat? + ( + [%p + collection + |> result_pat_for_collection(~loc) + ], + [%p + collection + |> error_pat_for_fields_of_collection_in_multi_field_form_or_single_field_form_with_collection_validator( + ~loc, + ) + ], + ) + ] + | Ok(None) => + collection + |> error_pat_for_fields_of_collection_in_multi_field_form_or_single_field_form_with_collection_validator( + ~loc, + ) + } + }; + switch (scheme) { + | [x] => x |> entry_of_one + | _ => + scheme |> List.map(entry_of_many) |> Pat.tuple + }; + }; + let expr = { + let fields_statuses = + Exp.record( + scheme + |> List.map((entry: Scheme.entry) => + switch (entry) { + | Field(field) => + field + |> field_dirty_status_record_field( + ~loc, + ) + | Collection({collection}) => + collection + |> collection_statuses_record_field( + ~loc, + ) + } + ), + None, + ); + + switch (collections) { + | [] => + %expr + Invalid({ + fieldsStatuses: [%e fields_statuses], + collectionsStatuses: (), + }) + | _ => + %expr + Invalid({ + fieldsStatuses: [%e fields_statuses], + collectionsStatuses: [%e + collections + |> collections_statuses_record(~loc) + ], + }) + }; + }; + Exp.case(pat, expr); + }; + + Exp.match(match_values, [ok_case, error_case]); + }; + } + ], + ), + ), + ), + ) + ] + ]; + }; +}; + +module Async = { + type validating_entry = [ + | `AsyncField(Scheme.field) + | `Collection(Collection.t) + ]; + + let ast = (~scheme: Scheme.t, ~loc) => { + [%stri + let validateForm = + ( + input: input, + ~validators: validators, + ~fieldsStatuses: fieldsStatuses, + ) + : Async.formValidationResult( + output, + fieldsStatuses, + collectionsStatuses, + ) => { + %e + { + let collections = scheme |> Scheme.collections; + + let match_values = { + let value = (entry: Scheme.entry) => + switch (entry) { + | Field( + { + validator: + SyncValidator( + Ok(Required | Optional(Some(_))) | Error (), + ), + } as field, + ) => + validate_field_with_sync_validator(~field, ~loc) + | Field( + {validator: SyncValidator(Ok(Optional(None)))} as field, + ) => + validate_field_without_validator(~field, ~loc) + | Field({validator: AsyncValidator(_)} as field) => + validate_field_with_async_validator(~field, ~loc) + | Collection({collection, fields, validator, output_type}) => + switch (validator) { + | Ok(Some ()) + | Error () => + %expr + ( + [%e validate_whole_collection(~collection, ~loc)], + [%e + validate_fields_of_collection_in_async_form( + ~collection, + ~fields, + ~output_type, + ~loc, + ) + ], + ) + | Ok(None) => + validate_fields_of_collection_in_async_form( + ~collection, + ~fields, + ~output_type, + ~loc, + ) + } + }; + switch (scheme) { + | [x] => x |> value + | _ => scheme |> List.map(value) |> Exp.tuple + }; + }; + + let validating_case = { + let pat = { + let entries_might_be_in_validating_state: list(validating_entry) = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field({validator: SyncValidator(_)}) => acc + | Field({validator: AsyncValidator(_)} as field) => [ + `AsyncField(field), + ...acc, + ] + | Collection({collection}) => [ + `Collection(collection), + ...acc, + ] + }, + [], + ); + let make = (entry: validating_entry) => + switch (entry) { + | `AsyncField(current_field) => + let entry = (entry: Scheme.entry) => + switch (entry) { + | Field({validator: AsyncValidator(_)} as field) + when field.name == current_field.name => + Pat.tuple([ + Pat.alias( + Pat.variant("Validating", Some(Pat.any())), + field_result_var(~field=field.name) |> str(~loc), + ), + Pat.var( + field_result_visibility_var(~field=field.name) + |> str(~loc), + ), + ]) + | Field( + {validator: SyncValidator(_) | AsyncValidator(_)} as field, + ) => + field |> result_and_visibility_pat_for_field(~loc) + | Collection({collection, validator}) => + switch (validator) { + | Ok(Some ()) + | Error () => [%pat? + ( + [%p collection |> result_pat_for_collection(~loc)], + [%p + collection + |> result_pat_for_fields_of_collection(~loc) + ], + ) + ] + | Ok(None) => + collection + |> result_pat_for_fields_of_collection(~loc) + } + }; + switch (scheme) { + | [x] => x |> entry + | _ => scheme |> List.map(entry) |> Pat.tuple + }; + | `Collection(current_collection) => + let entry = (entry: Scheme.entry) => + switch (entry) { + | Field(field) => + field |> result_and_visibility_pat_for_field(~loc) + | Collection({collection, validator}) + when collection.plural == current_collection.plural => + switch (validator) { + | Ok(Some ()) + | Error () => [%pat? + ( + [%p collection |> result_pat_for_collection(~loc)], + [%p + Pat.alias( + Pat.variant( + "ValidatingFieldsOfCollection", + Some(Pat.any()), + ), + collection + |> fields_of_collection_result_var + |> str(~loc), + ) + ], + ) + ] + | Ok(None) => + Pat.alias( + Pat.variant( + "ValidatingFieldsOfCollection", + Some(Pat.any()), + ), + collection + |> fields_of_collection_result_var + |> str(~loc), + ) + } + | Collection({collection, validator}) => + switch (validator) { + | Ok(Some ()) + | Error () => [%pat? + ( + [%p collection |> result_pat_for_collection(~loc)], + [%p + collection + |> result_pat_for_fields_of_collection(~loc) + ], + ) + ] + | Ok(None) => + collection + |> result_pat_for_fields_of_collection(~loc) + } + }; + switch (scheme) { + | [x] => x |> entry + | _ => scheme |> List.map(entry) |> Pat.tuple + }; + }; + switch (entries_might_be_in_validating_state) { + | [] => + failwith( + "No entries found that might be in validating state. Please, file an issue with your use-case.", + ) + | [x] => x |> make + | [x, ...rest] => P.or_(~pat=x |> make, ~make, rest) + }; + }; + let expr = { + let fields_statuses = + Exp.record( + scheme + |> List.map((entry: Scheme.entry) => + switch (entry) { + | Field({validator: SyncValidator(_)} as field) => + field |> field_dirty_status_record_field(~loc) + | Field({validator: AsyncValidator(_)} as field) => + field + |> async_field_dirty_or_validating_status_record_field( + ~loc, + ) + | Collection({collection}) => + collection + |> collection_that_might_be_in_validating_state_status_record_field( + ~loc, + ) + } + ), + None, + ); + switch (collections) { + | [] => + %expr + { + Validating({ + fieldsStatuses: [%e fields_statuses], + collectionsStatuses: (), + }); + } + | collections => + %expr + { + Validating({ + fieldsStatuses: [%e fields_statuses], + collectionsStatuses: [%e + collections |> collections_statuses_record(~loc) + ], + }); + } + }; + }; + Exp.case(pat, expr); + }; + + let ok_case = { + let pat = { + let entry = (entry: Scheme.entry) => + switch (entry) { + | Field({validator: SyncValidator(_)} as field) => + field |> ok_pat_for_sync_field(~loc) + | Field({validator: AsyncValidator(_)} as field) => + field |> ok_pat_for_async_field(~loc) + | Collection({collection, validator}) => + switch (validator) { + | Ok(Some ()) + | Error () => [%pat? + ( + [%p collection |> ok_pat_for_collection(~loc)], + [%p + collection + |> ok_pat_for_fields_of_async_collection(~loc) + ], + ) + ] + | Ok(None) => + collection |> ok_pat_for_fields_of_async_collection(~loc) + } + }; + switch (scheme) { + | [x] => x |> entry + | _ => scheme |> List.map(entry) |> Pat.tuple + }; + }; + let expr = { + let output = + Exp.record( + scheme + |> List.map((entry: Scheme.entry) => + switch (entry) { + | Field(field) => + field |> output_field_record_field(~loc) + | Collection({collection}) => + collection |> output_collection_record_field(~loc) + } + ), + None, + ); + let fields_statuses = + Exp.record( + scheme + |> List.map((entry: Scheme.entry) => + switch (entry) { + | Field(field) => + field |> field_dirty_status_record_field(~loc) + | Collection({collection}) => + collection |> collection_statuses_record_field(~loc) + } + ), + None, + ); + switch (collections) { + | [] => + %expr + Valid({ + output: [%e output], + fieldsStatuses: [%e fields_statuses], + collectionsStatuses: (), + }) + | collections => + %expr + Valid({ + output: [%e output], + fieldsStatuses: [%e fields_statuses], + collectionsStatuses: [%e + collections |> collections_statuses_record(~loc) + ], + }) + }; + }; + Exp.case(pat, expr); + }; + + let error_case = { + let pat = { + let entry_of_one = (entry: Scheme.entry) => + switch (entry) { + | Field(field) => + field + |> error_pat_for_async_field_in_single_field_form(~loc) + | Collection({collection, validator}) => + switch (validator) { + | Ok(Some ()) + | Error () => [%pat? + ( + [%p collection |> result_pat_for_collection(~loc)], + [%p + collection + |> error_pat_for_fields_statuses_of_async_collection( + ~loc, + ) + ], + ) + ] + | Ok(None) => + collection + |> error_pat_for_fields_of_collection_in_single_field_async_form_without_collection_validator( + ~loc, + ) + } + }; + let entry_of_many = (entry: Scheme.entry) => + switch (entry) { + | Field({validator: SyncValidator(_)} as field) => + field |> result_and_visibility_pat_for_field(~loc) + | Field({validator: AsyncValidator(_)} as field) => + field |> error_pat_for_async_field_in_multi_field_form(~loc) + | Collection({collection, validator}) => + switch (validator) { + | Ok(Some ()) + | Error () => [%pat? + ( + [%p collection |> result_pat_for_collection(~loc)], + [%p + collection + |> error_pat_for_fields_statuses_of_async_collection( + ~loc, + ) + ], + ) + ] + | Ok(None) => + collection + |> error_pat_for_fields_statuses_of_async_collection(~loc) + } + }; + switch (scheme) { + | [x] => x |> entry_of_one + | _ => scheme |> List.map(entry_of_many) |> Pat.tuple + }; + }; + let expr = { + let fields_statuses = + Exp.record( + scheme + |> List.map((entry: Scheme.entry) => + switch (entry) { + | Field(field) => + field |> field_dirty_status_record_field(~loc) + | Collection({collection}) => + collection |> collection_statuses_record_field(~loc) + } + ), + None, + ); + + switch (collections) { + | [] => + %expr + Invalid({ + fieldsStatuses: [%e fields_statuses], + collectionsStatuses: (), + }) + | collections => + %expr + Invalid({ + fieldsStatuses: [%e fields_statuses], + collectionsStatuses: [%e + collections |> collections_statuses_record(~loc) + ], + }) + }; + }; + Exp.case(pat, expr); + }; + + Exp.match(match_values, [validating_case, ok_case, error_case]); + }; + } + ]; + }; +}; diff --git a/lib/ppx/Form_ValidatorsRecord.re b/lib/ppx/Form_ValidatorsRecord.re new file mode 100644 index 00000000..c8a9e9b4 --- /dev/null +++ b/lib/ppx/Form_ValidatorsRecord.re @@ -0,0 +1,369 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let ensure_eq = (~loc, fields) => + if (fields + |> List.exists((({txt: lid}, _)) => + switch (lid) { + | Lident("eq") => true + | _ => false + } + )) { + fields; + } else { + [(Lident("eq") |> lid(~loc), [%expr (==)]), ...fields]; + }; + +let update_async_validator_of_field = + ( + ~field: string, + ~output_type: ItemType.t, + ~async_mode: AsyncMode.t, + ~validator_loc: Location.t, + fields, + ) => + fields + |> ensure_eq(~loc=validator_loc) + |> List.map(((v_lid, {pexp_loc: loc} as expr)) => + switch (v_lid) { + | {txt: Lident("validateAsync")} => + let fn = [%expr + ( + ((value, dispatch)) => { + let validate: + Async.validateAsyncFn( + [%t output_type |> ItemType.unpack], + message, + ) = [%e + expr + ]; + Async.validateAsync(~value, ~validate, ~andThen=res => { + dispatch( + [%e + Exp.construct( + Lident(FieldPrinter.apply_async_result_action(~field)) + |> lid(~loc), + Some( + Exp.tuple([ + Exp.ident(Lident("value") |> lid(~loc)), + Exp.ident(Lident("res") |> lid(~loc)), + ]), + ), + ) + ], + ) + }); + } + ) + ]; + ( + v_lid, + switch (async_mode) { + | OnBlur => fn + | OnChange => + %expr + Debouncer.make(~wait=debounceInterval, [%e fn]) + }, + ); + | _ => (v_lid, expr) + } + ); + +let update_async_validator_of_field_of_collection = + ( + ~field: string, + ~collection: Collection.t, + ~output_type: ItemType.t, + ~async_mode: AsyncMode.t, + ~validator_loc: Location.t, + fields, + ) => + fields + |> ensure_eq(~loc=validator_loc) + |> List.map(((v_lid, {pexp_loc: loc} as expr)) => + switch (v_lid) { + | {txt: Lident("validateAsync")} => + let fn = [%expr + ( + ((value, index, dispatch)) => { + let validate: + Async.validateAsyncFn( + [%t output_type |> ItemType.unpack], + message, + ) = [%e + expr + ]; + Async.validateAsync(~value, ~validate, ~andThen=res => { + dispatch( + [%e + Exp.construct( + Lident( + FieldOfCollectionPrinter.apply_async_result_action( + ~collection, + ~field, + ), + ) + |> lid(~loc), + Some( + Exp.tuple([ + Exp.ident(Lident("value") |> lid(~loc)), + Exp.ident(Lident("index") |> lid(~loc)), + Exp.ident(Lident("res") |> lid(~loc)), + ]), + ), + ) + ], + ) + }); + } + ) + ]; + ( + v_lid, + switch (async_mode) { + | OnBlur => fn + | OnChange => + %expr + Debouncer.make(~wait=debounceInterval, [%e fn]) + }, + ); + | _ => (v_lid, expr) + } + ); + +// What we need to do here: +// 1. Update values of optional validators: set them to () instead of None +// 2. Wrap async validators so each dispatches appropriate action +// 3. Debounce async validators that run on change +// 4. Don't touch unknown, let compiler do its job +let ast = + ( + scheme: Scheme.t, + validators_record: ValidatorsRecord.t, + value_binding: value_binding, + ) => { + let fields = + validators_record.fields + |> List.map(((f_lid, expr)) => + switch (f_lid) { + | {txt: Lident(key)} => + let entry = + scheme + |> List.find_opt( + fun + | Scheme.Field(field) => field.name == key + | Scheme.Collection({collection}) => + collection.plural == key, + ); + switch (entry) { + | None => (f_lid, expr) + | Some(Field(field)) => + switch (field.validator) { + | SyncValidator(Ok(Required)) => (f_lid, expr) + | SyncValidator(Ok(Optional(Some ()))) => (f_lid, expr) + | SyncValidator(Ok(Optional(None))) => + let loc = expr.pexp_loc; + (f_lid, [%expr ()]); + | SyncValidator(Error ()) => (f_lid, expr) + | AsyncValidator({mode: async_mode}) => ( + f_lid, + switch (expr) { + | { + pexp_desc: Pexp_record(fields, None), + pexp_loc, + pexp_loc_stack, + pexp_attributes, + } => { + pexp_desc: + Pexp_record( + fields + |> update_async_validator_of_field( + ~field=field.name, + ~output_type=field.output_type, + ~async_mode, + ~validator_loc=pexp_loc, + ), + None, + ), + pexp_loc, + pexp_loc_stack, + pexp_attributes, + } + | _ => expr + }, + ) + } + | Some( + Collection({ + collection, + fields: collection_fields, + validator: collection_validator, + }), + ) => ( + f_lid, + switch (expr) { + | { + pexp_desc: Pexp_record(collection_validator_fields, None), + pexp_loc, + pexp_loc_stack, + pexp_attributes, + } => + let fields = + collection_validator_fields + |> List.map(((c_lid, expr)) => + switch (c_lid) { + | {txt: Lident("collection")} => ( + c_lid, + switch (collection_validator) { + | Ok(Some ()) + | Error () => expr + | Ok(None) => + let loc = expr.pexp_loc; + %expr + (); + }, + ) + | {txt: Lident("fields")} => ( + c_lid, + switch (expr) { + | { + pexp_desc: + Pexp_record(field_validator_fields, None), + pexp_loc, + pexp_loc_stack, + pexp_attributes, + } => + let fields = + field_validator_fields + |> List.map(((f_lid, expr)) => + switch (f_lid) { + | {txt: Lident(key)} => + let field = + collection_fields + |> List.find_opt( + (field: Scheme.field) => + field.name == key + ); + switch (field) { + | None => (f_lid, expr) + | Some({ + validator: + SyncValidator(Ok(Required)), + }) => ( + f_lid, + expr, + ) + | Some({ + validator: + SyncValidator( + Ok(Optional(Some ())), + ), + }) => ( + f_lid, + expr, + ) + | Some({ + validator: + SyncValidator( + Ok(Optional(None)), + ), + }) => + let loc = expr.pexp_loc; + (f_lid, [%expr ()]); + | Some({ + validator: SyncValidator(Error ()), + }) => ( + f_lid, + expr, + ) + | Some( + { + validator: + AsyncValidator({ + mode: async_mode, + }), + } as field, + ) => ( + f_lid, + switch (expr) { + | { + pexp_desc: + Pexp_record(fields, None), + pexp_loc, + pexp_loc_stack, + pexp_attributes, + } => { + pexp_desc: + Pexp_record( + fields + |> update_async_validator_of_field_of_collection( + ~field=field.name, + ~collection, + ~output_type= + field.output_type, + ~async_mode, + ~validator_loc=pexp_loc, + ), + None, + ), + pexp_loc, + pexp_loc_stack, + pexp_attributes, + } + | _ => expr + }, + ) + }; + | {txt: _} => (f_lid, expr) + } + ); + { + pexp_desc: Pexp_record(fields, None), + pexp_loc, + pexp_loc_stack, + pexp_attributes, + }; + | _ => expr + }, + ) + | _ => (c_lid, expr) + } + ); + + { + pexp_desc: Pexp_record(fields, None), + pexp_loc, + pexp_loc_stack, + pexp_attributes, + }; + | _ => expr + }, + ) + }; + | _ => (f_lid, expr) + } + ); + { + ...value_binding, + pvb_expr: { + pexp_desc: + Pexp_constraint( + { + pexp_desc: Pexp_record(fields, None), + pexp_loc: validators_record.record_metadata.pexp_loc, + pexp_loc_stack: validators_record.record_metadata.pexp_loc_stack, + pexp_attributes: validators_record.record_metadata.pexp_attributes, + }, + validators_record.annotation, + ), + pexp_loc: validators_record.constraint_metadata.pexp_loc, + pexp_loc_stack: validators_record.constraint_metadata.pexp_loc_stack, + pexp_attributes: validators_record.constraint_metadata.pexp_attributes, + }, + }; +}; diff --git a/lib/ppx/Form_ValidatorsType.re b/lib/ppx/Form_ValidatorsType.re new file mode 100644 index 00000000..87223af9 --- /dev/null +++ b/lib/ppx/Form_ValidatorsType.re @@ -0,0 +1,133 @@ +open Meta; +open Ast; +open AstHelpers; +open Printer; + +open Ppxlib; +open Ast_helper; + +let field_type = (~loc, field: Scheme.field) => + Type.field( + field.name |> str(~loc), + switch (field.validator) { + | SyncValidator(Ok(Required)) + | SyncValidator(Ok(Optional(Some(_)))) + | SyncValidator(Error ()) => [%type: + singleValueValidator( + input, + [%t field.output_type |> ItemType.unpack], + message, + ) + ] + | SyncValidator(Ok(Optional(None))) => [%type: unit] + | AsyncValidator(_) => [%type: + Async.singleValueValidator( + input, + [%t field.output_type |> ItemType.unpack], + message, + action, + ) + ] + }, + ); + +let collection_type = + (~loc, ~validator: CollectionValidator.t, collection: Collection.t) => + Type.field( + collection.plural |> str(~loc), + switch (validator) { + | Ok(Some ()) + | Error () => [%type: + collectionValidatorWithWholeCollectionValidator( + input, + message, + [%t + Typ.constr( + Lident(collection |> CollectionPrinter.validator_type) + |> lid(~loc), + [], + ) + ], + ) + ] + | Ok(None) => [%type: + collectionValidatorWithoutWholeCollectionValidator( + [%t + Typ.constr( + Lident(collection |> CollectionPrinter.validator_type) + |> lid(~loc), + [], + ) + ], + ) + ] + }, + ); + +let field_of_collection_type = (~loc, field: Scheme.field) => + Type.field( + field.name |> str(~loc), + switch (field.validator) { + | SyncValidator(Ok(Required)) + | SyncValidator(Ok(Optional(Some(_)))) + | SyncValidator(Error ()) => [%type: + valueOfCollectionValidator( + input, + [%t field.output_type |> ItemType.unpack], + message, + ) + ] + | SyncValidator(Ok(Optional(None))) => [%type: unit] + | AsyncValidator(_) => [%type: + Async.valueOfCollectionValidator( + input, + [%t field.output_type |> ItemType.unpack], + message, + action, + ) + ] + }, + ); + +let ast = (~scheme: Scheme.t, ~loc) => { + let main_decl = + "validators" + |> str(~loc) + |> Type.mk( + ~kind= + Ptype_record( + scheme + |> List.map((entry: Scheme.entry) => + switch (entry) { + | Field(field) => field |> field_type(~loc) + | Collection({collection, validator}) => + collection |> collection_type(~validator, ~loc) + } + ), + ), + ); + + let collections_decls = + scheme + |> List.fold_left( + (acc, entry: Scheme.entry) => + switch (entry) { + | Field(_) => acc + | Collection({collection, fields}) => [ + collection + |> CollectionPrinter.validator_type + |> str(~loc) + |> Type.mk( + ~kind= + Ptype_record( + fields |> List.map(field_of_collection_type(~loc)), + ), + ), + ...acc, + ] + }, + [], + ); + + Str.type_(~loc, Recursive, [main_decl, ...collections_decls]); +}; diff --git a/lib/ppx/Meta.re b/lib/ppx/Meta.re new file mode 100644 index 00000000..05aa80c9 --- /dev/null +++ b/lib/ppx/Meta.re @@ -0,0 +1,2390 @@ +open Ast; + +open Ppxlib; + +module ItemType = { + module T: {type t;} = { + type t = core_type; + }; + + type t = T.t; + + external make: core_type => t = "%identity"; + external unpack: t => core_type = "%identity"; + + let rec eq = (t1: core_type, t2: core_type) => + switch (t1.ptyp_desc, t2.ptyp_desc) { + | (Ptyp_constr({txt: lid1}, list1), Ptyp_constr({txt: lid2}, list2)) => + eq_lid(lid1, lid2) && eq_list(list1, list2) + | (Ptyp_var(x1), Ptyp_var(x2)) => x1 == x2 + | (Ptyp_tuple(l1), Ptyp_tuple(l2)) => eq_list(l1, l2) + | _ => false + } + and eq_lid = (l1: Longident.t, l2: Longident.t) => + switch (l1, l2) { + | (Lident(x1), Lident(x2)) => x1 == x2 + | (Ldot(l1, x1), Ldot(l2, x2)) => x1 == x2 && eq_lid(l1, l2) + | (Lapply(l1, l1'), Lapply(l2, l2')) => + eq_lid(l1, l2) && eq_lid(l1', l2') + | _ => false + } + and eq_list = (l1: list(core_type), l2: list(core_type)) => + if (List.length(l1) == List.length(l2)) { + List.for_all2((t1, t2) => eq(t1, t2), l1, l2); + } else { + false; + }; + let eq = (x1: t, x2: t) => eq(x1 |> unpack, x2 |> unpack); +}; + +module Collection = { + type t = { + singular: string, + plural: string, + }; +}; + +module FieldDep = { + type t = + | DepField(string) + | DepFieldOfCollection({ + collection: Collection.t, + field: string, + }); + + type unvalidated = + | UnvalidatedDepField({ + name: string, + loc: Location.t, + }) + | UnvalidatedDepFieldOfCollection({ + collection: string, + field: string, + c_loc: Location.t, + f_loc: Location.t, + }); +}; + +module FieldOptionality = { + type t = + | OptionType + | StringType + | OptionStringType; +}; + +module AsyncMode = { + type t = + | OnChange + | OnBlur; + + let default = OnChange; +}; + +module ValidatorsRecord = { + type t = { + fields, + rec_flag, + constraint_metadata: metadata, + record_metadata: metadata, + annotation: core_type, + } + and fields = list((loc(Longident.t), expression)) + and metadata = { + pexp_loc: Location.t, + pexp_loc_stack: list(Location.t), + pexp_attributes: list(attribute), + }; +}; + +module FieldValidator = { + type t = + | SyncValidator(result(sync, unit)) + | AsyncValidator({ + mode: AsyncMode.t, + optionality: option(FieldOptionality.t), + }) + and sync = + | Required + | Optional(option(unit)); +}; + +module CollectionValidator = { + type t = result(option(unit), unit); +}; + +module Scheme = { + type t = list(entry) + and entry = + | Field(field) + | Collection(collection) + and field = { + name: string, + input_type: ItemType.t, + output_type: ItemType.t, + validator: FieldValidator.t, + deps: list(FieldDep.t), + } + and collection = { + collection: Collection.t, + fields: list(field), + validator: CollectionValidator.t, + input_type: ItemType.t, + output_type: ItemType.t, + }; + + let collections = (scheme: t) => + scheme + |> List.fold_left( + (acc, entry) => + switch (entry) { + | Field(_) => acc + | Collection(collection) => [collection, ...acc] + }, + [], + ); +}; + +module InputFieldData = { + type unvalidated = { + name: string, + typ: ItemType.t, + async: option(AsyncMode.t), + deps: list(FieldDep.unvalidated), + }; + + type validated = { + name: string, + typ: ItemType.t, + async: option(AsyncMode.t), + deps: list(FieldDep.t), + }; + + let unvalidated = (~async, ~deps, field: label_declaration): unvalidated => { + name: field.pld_name.txt, + typ: field.pld_type |> ItemType.make, + async, + deps, + }; + + let validated = (~deps, field: unvalidated): validated => { + name: field.name, + typ: field.typ, + async: field.async, + deps, + }; +}; + +module InputField = { + type unvalidated = + | UnvalidatedInputField(InputFieldData.unvalidated) + | UnvalidatedInputFieldOfCollection({ + collection: Collection.t, + field: InputFieldData.unvalidated, + }); + + type validated = + | ValidatedInputField(InputFieldData.validated) + | ValidatedInputFieldOfCollection({ + collection: Collection.t, + field: InputFieldData.validated, + }); +}; + +module OutputFieldData = { + type t = { + name: string, + typ: ItemType.t, + loc: Location.t, + }; +}; + +module OutputField = { + type t = + | OutputField(OutputFieldData.t) + | OutputFieldOfCollection({ + collection: Collection.t, + field: OutputFieldData.t, + }); +}; + +module InputType = { + module T: {type t;} = { + type t = type_declaration; + }; + + type t = T.t; + external make: type_declaration => t = "%identity"; + external type_declaration: t => type_declaration = "%identity"; +}; + +module OutputType = { + module T: {type t;} = { + type t = type_declaration; + }; + + type t = T.t; + external make: type_declaration => t = "%identity"; + external type_declaration: t => type_declaration = "%identity"; + + let default = (~loc) => [%stri type output = input]; +}; + +module MessageType = { + let default = (~loc) => [%stri type message = string]; +}; + +module DebounceInterval = { + let default = (~loc) => [%stri let debounceInterval = 700]; +}; + +module SubmissionErrorType = { + let default = (~loc) => [%stri type submissionError = unit]; +}; + +module FieldOptionalityParser = { + let parse = (typ: ItemType.t): option(FieldOptionality.t) => + switch (typ |> ItemType.unpack) { + | {ptyp_desc: Ptyp_constr({txt: Lident("string")}, [])} => + Some(StringType) + | { + ptyp_desc: + Ptyp_constr( + {txt: Lident("option")}, + [{ptyp_desc: Ptyp_constr({txt: Lident("string")}, [])}], + ), + } => + Some(OptionStringType) + | {ptyp_desc: Ptyp_constr({txt: Lident("option")}, _)} => + Some(OptionType) + | _ => None + }; +}; + +module AsyncFieldParser = { + type error = + | InvalidPayload(Location.t) + | InvalidAsyncMode(Location.t); + + let attr = field => + field.pld_type.ptyp_attributes + |> List.find_opt(attr => + switch (attr) { + | {attr_name: {txt: "field.async"}} => true + | _ => false + } + ); + + let parse = attribute => { + switch (attribute) { + | {attr_payload: PStr([]), attr_loc} => Ok(AsyncMode.default) + | { + attr_payload: + PStr([ + { + pstr_desc: + Pstr_eval( + { + pexp_desc: + Pexp_record( + [ + ( + {txt: Lident("mode")}, + { + pexp_desc: + Pexp_construct( + {txt: Lident(mode), loc}, + None, + ), + }, + ), + ], + None, + ), + }, + _, + ), + }, + ]), + attr_loc, + } => + switch (mode) { + | "OnChange" => Ok(OnChange) + | "OnBlur" => Ok(OnBlur) + | _ => Error(InvalidAsyncMode(loc)) + } + | {attr_payload: PStr([{pstr_loc}])} => + Error(InvalidPayload(pstr_loc)) + | {attr_loc} => Error(InvalidPayload(attr_loc)) + }; + }; + + let get = field => + switch (field |> attr) { + | None => Ok(None) + | Some(attr) => + switch (attr |> parse) { + | Ok(mode) => Ok(Some(mode)) + | Error(error) => Error(error) + } + }; +}; + +module FieldDepsParser = { + type error = + | DepsParseError(Location.t) + | DepNotFound(FieldDep.unvalidated) + | DepOfItself([ | `Field(string, Location.t)]) + | DepDuplicate(FieldDep.unvalidated); + + let attr = field => + field.pld_type.ptyp_attributes + |> List.find_opt(attr => + switch (attr) { + | {attr_name: {txt: "field.deps"}} => true + | _ => false + } + ); + + let parse = + (attribute: attribute): result(list(FieldDep.unvalidated), error) => { + switch (attribute) { + | {attr_payload: PStr([{pstr_desc: Pstr_eval(exp, _)}]), attr_loc} => + switch (exp) { + | {pexp_desc: Pexp_ident({txt: Lident(dep), loc})} => + Ok([UnvalidatedDepField({name: dep, loc})]) + | { + pexp_desc: + Pexp_field( + { + pexp_desc: Pexp_ident({txt: Lident(collection), loc: c_loc}), + }, + {txt: Lident(field), loc: f_loc}, + ), + } => + Ok([ + UnvalidatedDepFieldOfCollection({collection, field, c_loc, f_loc}), + ]) + | {pexp_desc: Pexp_tuple(exps)} => + exps + |> List.fold_left( + (res: result(list(FieldDep.unvalidated), error), exp) => + switch (res, exp) { + | (Error(error), _) => Error(error) + | ( + Ok(deps), + {pexp_desc: Pexp_ident({txt: Lident(dep), loc})}, + ) => + Ok([UnvalidatedDepField({name: dep, loc}), ...deps]) + | ( + Ok(deps), + { + pexp_desc: + Pexp_field( + { + pexp_desc: + Pexp_ident({ + txt: Lident(collection), + loc: c_loc, + }), + }, + {txt: Lident(field), loc: f_loc}, + ), + }, + ) => + Ok([ + UnvalidatedDepFieldOfCollection({ + collection, + field, + c_loc, + f_loc, + }), + ...deps, + ]) + | (Ok(_), {pexp_loc}) => Error(DepsParseError(pexp_loc)) + }, + Ok([]), + ) + | {pexp_loc} => Error(DepsParseError(pexp_loc)) + } + | {attr_loc} => Error(DepsParseError(attr_loc)) + }; + }; + + let get = field => + switch (field |> attr) { + | None => Ok([]) + | Some(attr) => attr |> parse + }; +}; + +module FieldCollectionParser = { + type result = Pervasives.result(ok, error) + and ok = { + collection: Collection.t, + fields: list(InputFieldData.unvalidated), + input_type: ItemType.t, + } + and error = + | NotArray(Location.t) + | InvalidTypeRef(Location.t) + | RecordNotFound(Location.t) + | NotRecord(Location.t) + | InvalidAsyncField(AsyncFieldParser.error) + | InvalidFieldDeps(FieldDepsParser.error); + + let attr = (field: label_declaration) => + field.pld_type.ptyp_attributes + |> List.find_opt(attr => + switch (attr) { + | {attr_name: {txt: "field.collection"}} => true + | _ => false + } + ); + + let parse = (~structure: structure, field: label_declaration): result => { + switch (field.pld_type.ptyp_desc) { + | Ptyp_constr({txt: Lident("array"), loc: arr_loc}, payload) => + switch (payload) { + | [] => Error(InvalidTypeRef(arr_loc)) + | [ + {ptyp_desc: Ptyp_constr({txt: Lident(typ_name)}, []), ptyp_loc} as input_type, + ..._, + ] => + let record_type = ref(None); + structure + |> List.iter((item: structure_item) => + switch (item) { + | {pstr_desc: Pstr_type(rec_flag, decls)} => + decls + |> List.iter((decl: type_declaration) => + switch (decl) { + | {ptype_name: {txt: name}} when name == typ_name => + switch (decl.ptype_kind) { + | Ptype_record(fields) => + record_type := Some(Ok(fields)) + | _ => + record_type := + Some(Error(NotRecord(decl.ptype_loc))) + } + | _ => () + } + ) + | _ => () + } + ); + switch (record_type^) { + | None => Error(RecordNotFound(ptyp_loc)) + | Some(Error(error)) => Error(error) + | Some(Ok(fields)) => + let fields = + fields + |> List.fold_left( + (res, field: label_declaration) => + switch (res) { + | Error(error) => Error(error) + | Ok(fields) => + switch ( + field |> AsyncFieldParser.get, + field |> FieldDepsParser.get, + ) { + | (Ok(async), Ok(deps)) => + Ok([ + field |> InputFieldData.unvalidated(~async, ~deps), + ...fields, + ]) + | (Error(error), _) => Error(InvalidAsyncField(error)) + | (_, Error(error)) => Error(InvalidFieldDeps(error)) + } + }, + Ok([]), + ); + switch (fields) { + | Ok(fields) => + Ok({ + collection: { + plural: field.pld_name.txt, + singular: typ_name, + }, + fields, + input_type: input_type |> ItemType.make, + }) + | Error(error) => Error(error) + }; + }; + | [{ptyp_loc}, ..._] => Error(InvalidTypeRef(ptyp_loc)) + } + | _ => Error(NotArray(field.pld_loc)) + }; + }; +}; + +module FieldAttributesParser = { + type result = Pervasives.result(option(ok), error) + and ok = + | Collection(FieldCollectionParser.ok) + | AsyncDeps({ + async: option(AsyncMode.t), + deps: list(FieldDep.unvalidated), + }) + and error = + | Conflict( + [ + | `AsyncWithCollection(Location.t) + | `DepsWithCollection(Location.t) + ], + ) + | InvalidCollectionField(FieldCollectionParser.error) + | InvalidAsyncField(AsyncFieldParser.error) + | InvalidFieldDeps(FieldDepsParser.error); + + let parse = (~structure: structure, field: label_declaration) => + switch ( + field |> FieldCollectionParser.attr, + field |> AsyncFieldParser.attr, + field |> FieldDepsParser.attr, + ) { + | (Some(_), None, None) => + switch (field |> FieldCollectionParser.parse(~structure)) { + | Ok(collection) => Ok(Some(Collection(collection))) + | Error(error) => Error(InvalidCollectionField(error)) + } + | (None, Some(async_attr), Some(deps_attr)) => + switch ( + async_attr |> AsyncFieldParser.parse, + deps_attr |> FieldDepsParser.parse, + ) { + | (Ok(async), Ok(deps)) => + Ok(Some(AsyncDeps({async: Some(async), deps}))) + | (Error(error), _) => Error(InvalidAsyncField(error)) + | (_, Error(error)) => Error(InvalidFieldDeps(error)) + } + | (None, Some(async_attr), None) => + switch (async_attr |> AsyncFieldParser.parse) { + | Ok(async) => Ok(Some(AsyncDeps({async: Some(async), deps: []}))) + | Error(error) => Error(InvalidAsyncField(error)) + } + | (None, None, Some(deps_attr)) => + switch (deps_attr |> FieldDepsParser.parse) { + | Ok(deps) => Ok(Some(AsyncDeps({async: None, deps}))) + | Error(error) => Error(InvalidFieldDeps(error)) + } + | (None, None, None) => Ok(None) + | (Some(_), Some({attr_loc}), _) => + Error(Conflict(`AsyncWithCollection(attr_loc))) + | (Some(_), _, Some({attr_loc})) => + Error(Conflict(`DepsWithCollection(attr_loc))) + }; +}; + +module InputTypeParser = { + type result = Pervasives.result(ok, error) + and ok = { + entries: list(unvalidated_entry), + type_declaration: InputType.t, + } + and unvalidated_entry = + | UnvalidatedInputField(InputFieldData.unvalidated) + | UnvalidatedInputCollection({ + collection: Collection.t, + fields: list(InputFieldData.unvalidated), + input_type: ItemType.t, + }) + and validated_entry = + | ValidatedInputField(InputFieldData.validated) + | ValidatedInputCollection({ + collection: Collection.t, + fields: list(InputFieldData.validated), + input_type: ItemType.t, + }) + and error = + | NotFound + | NotRecord(Location.t) + | InvalidAttributes(FieldAttributesParser.error); + + let parse = (~decl, ~structure, ~loc, fields) => { + let entries = + List.fold_right( + (field, res) => + switch (res, field |> FieldAttributesParser.parse(~structure)) { + | ( + Ok(entries), + Ok(Some(Collection({collection, fields, input_type}))), + ) => + Ok([ + UnvalidatedInputCollection({collection, fields, input_type}), + ...entries, + ]) + | (Ok(entries), Ok(Some(AsyncDeps({async, deps})))) => + Ok([ + UnvalidatedInputField( + field |> InputFieldData.unvalidated(~async, ~deps), + ), + ...entries, + ]) + | (Ok(entries), Ok(None)) => + Ok([ + UnvalidatedInputField( + field |> InputFieldData.unvalidated(~async=None, ~deps=[]), + ), + ...entries, + ]) + | (Error(error), _) => Error(error) + | (_, Error(error)) => Error(InvalidAttributes(error)) + }, + fields, + Ok([]), + ); + switch (entries) { + | Error(error) => Error(error) + | Ok(entries) => Ok({entries, type_declaration: decl |> InputType.make}) + }; + }; + + let validate = + (unvalidated_entries: list(unvalidated_entry)) + : Pervasives.result(list(validated_entry), FieldDepsParser.error) => { + let dup = (deps: list(FieldDep.unvalidated), dep: FieldDep.unvalidated) => + switch ( + deps + |> List.find_all((dep': FieldDep.unvalidated) => + switch (dep, dep') { + | ( + UnvalidatedDepField({name: dep}), + UnvalidatedDepField({name: dep'}), + ) => + dep == dep + | ( + UnvalidatedDepFieldOfCollection({collection, field}), + UnvalidatedDepFieldOfCollection({ + collection: collection', + field: field', + }), + ) => + collection == collection' && field == field' + | (UnvalidatedDepField(_), UnvalidatedDepFieldOfCollection(_)) + | (UnvalidatedDepFieldOfCollection(_), UnvalidatedDepField(_)) => + false + } + ) + |> List.length + ) { + | 0 + | 1 => None + | _ => Some() + }; + + unvalidated_entries + |> List.fold_left( + ( + res: + Pervasives.result(list(validated_entry), FieldDepsParser.error), + unvalidated_entry: unvalidated_entry, + ) => + switch (res, unvalidated_entry) { + | (Error(error), _) => Error(error) + | (Ok(validated_entries), UnvalidatedInputField(field)) => + let deps = + field.deps + |> List.fold_left( + ( + res: + Pervasives.result( + list(FieldDep.t), + FieldDepsParser.error, + ), + dep, + ) => + switch (res) { + | Error(error) => Error(error) + | Ok(validated_deps) => + switch (dep |> dup(field.deps)) { + | Some () => Error(FieldDepsParser.DepDuplicate(dep)) + | None => + switch ( + unvalidated_entries + |> List.fold_left( + ( + res: + option( + Pervasives.result( + FieldDep.t, + FieldDepsParser.error, + ), + ), + entry, + ) => + switch (res, dep, entry) { + | (Some(_) as res, _, _) => res + | ( + None, + UnvalidatedDepField(dep'), + UnvalidatedInputField(field'), + ) => + if (field.name == field'.name + && field'.name == dep'.name) { + Some( + Error( + FieldDepsParser.DepOfItself( + `Field((dep'.name, dep'.loc)), + ), + ), + ); + } else if (field'.name == dep'.name) { + Some(Ok(DepField(dep'.name))); + } else { + None; + } + | ( + None, + UnvalidatedDepFieldOfCollection(dep'), + UnvalidatedInputCollection(entry'), + ) => + if (dep'.collection + != entry'.collection.singular) { + None; + } else { + switch ( + entry'.fields + |> List.find_opt( + ( + field: InputFieldData.unvalidated, + ) => + dep'.field == field.name + ) + ) { + | None => + Some( + Error( + FieldDepsParser.DepNotFound(dep), + ), + ) + | Some(field) => + Some( + Ok( + DepFieldOfCollection({ + collection: entry'.collection, + field: field.name, + }), + ), + ) + }; + } + | ( + None, + UnvalidatedDepField(_), + UnvalidatedInputCollection(_), + ) + | ( + None, + UnvalidatedDepFieldOfCollection(_), + UnvalidatedInputField(_), + ) => + None + }, + None, + ) + ) { + | None => Error(FieldDepsParser.DepNotFound(dep)) + | Some(Error(error)) => Error(error) + | Some(Ok(dep_entry)) => + Ok([dep_entry, ...validated_deps]) + } + } + }, + Ok([]), + ); + switch (deps) { + | Error(error) => Error(error) + | Ok(deps) => + Ok([ + ValidatedInputField( + field |> InputFieldData.validated(~deps), + ), + ...validated_entries, + ]) + }; + | ( + Ok(validated_entries), + UnvalidatedInputCollection({ + collection, + fields: unvalidated_fields, + input_type, + }), + ) => + let validated_fields = + unvalidated_fields + |> List.fold_left( + ( + res: + Pervasives.result( + list(InputFieldData.validated), + FieldDepsParser.error, + ), + field: InputFieldData.unvalidated, + ) => + switch (res) { + | Error(error) => Error(error) + | Ok(validated_fields) => + let deps = + field.deps + |> List.fold_left( + ( + res: + Pervasives.result( + list(FieldDep.t), + FieldDepsParser.error, + ), + dep, + ) => + switch (res) { + | Error(error) => Error(error) + | Ok(validated_deps) => + switch (dep |> dup(field.deps)) { + | Some () => + Error(FieldDepsParser.DepDuplicate(dep)) + | None => + switch ( + unvalidated_entries + |> List.fold_left( + ( + res: + option( + Pervasives.result( + FieldDep.t, + FieldDepsParser.error, + ), + ), + entry, + ) => + switch (res, dep, entry) { + | (Some(_) as res, _, _) => res + | ( + None, + UnvalidatedDepField(dep'), + UnvalidatedInputField( + field', + ), + ) => + if (field'.name == dep'.name) { + Some( + Ok(DepField(dep'.name)), + ); + } else { + None; + } + | ( + None, + UnvalidatedDepFieldOfCollection( + dep', + ), + UnvalidatedInputCollection( + entry', + ), + ) => + if (dep'.collection + != entry'.collection. + singular) { + None; + } else { + switch ( + entry'.fields + |> List.fold_left( + ( + res: + option( + Pervasives.result( + FieldDep.t, + FieldDepsParser.error, + ), + ), + field: InputFieldData.unvalidated, + ) => + switch (res) { + | Some(_) => res + | None => + if (dep'.field + == field.name) { + Some( + Ok( + DepFieldOfCollection({ + collection: + entry'. + collection, + field: + field.name, + }), + ), + ); + } else { + None; + } + }, + None, + ) + ) { + | None => + Some( + Error( + FieldDepsParser.DepNotFound( + dep, + ), + ), + ) + | Some(Error(error)) => + Some(Error(error)) + | Some(Ok(dep)) => + Some(Ok(dep)) + }; + } + | ( + None, + UnvalidatedDepField(_), + UnvalidatedInputCollection( + _ + ), + ) + | ( + None, + UnvalidatedDepFieldOfCollection( + _ + ), + UnvalidatedInputField(_), + ) => + None + }, + None, + ) + ) { + | None => + Error( + FieldDepsParser.DepNotFound(dep), + ) + | Some(Error(error)) => Error(error) + | Some(Ok(dep_entry)) => + Ok([dep_entry, ...validated_deps]) + } + } + }, + Ok([]), + ); + switch (deps) { + | Error(error) => Error(error) + | Ok(deps) => + Ok([ + field |> InputFieldData.validated(~deps), + ...validated_fields, + ]) + }; + }, + Ok([]), + ); + switch (validated_fields) { + | Error(error) => Error(error) + | Ok(validated_fields) => + Ok([ + ValidatedInputCollection({ + collection, + fields: validated_fields, + input_type, + }), + ...validated_entries, + ]) + }; + }, + Ok([]), + ); + }; + + let in_deps_of = + (entries: list(validated_entry), field: InputField.validated) + : option(InputField.validated) => { + entries + |> List.fold_left( + (res, entry: validated_entry) => + switch (res, field, entry) { + | (Some(_), _, _) => res + | ( + None, + ValidatedInputField(subject_field), + ValidatedInputField(entry_field), + ) => + entry_field.deps + |> List.fold_left( + (res: option(InputField.validated), dep: FieldDep.t) => + switch (res, dep) { + | (Some(_), _) => res + | (None, DepField(dep)) => + if (dep == subject_field.name) { + Some(ValidatedInputField(entry_field)); + } else { + None; + } + | (None, DepFieldOfCollection(_)) => None + }, + None, + ) + | ( + None, + ValidatedInputField(subject_field), + ValidatedInputCollection({ + collection: entry_collection, + fields: entry_fields, + }), + ) => + entry_fields + |> List.fold_left( + ( + res: option(InputField.validated), + entry_field: InputFieldData.validated, + ) => + entry_field.deps + |> List.fold_left( + (res: option(InputField.validated), dep: FieldDep.t) => + switch (res, dep) { + | (Some(_), _) => res + | (None, DepField(dep)) => + if (dep == subject_field.name) { + Some( + ValidatedInputFieldOfCollection({ + collection: entry_collection, + field: entry_field, + }), + ); + } else { + None; + } + | (None, DepFieldOfCollection(_)) => None + }, + None, + ), + None, + ) + | ( + None, + ValidatedInputFieldOfCollection({ + collection: subject_collection, + field: subject_field, + }), + ValidatedInputField(entry_field), + ) => + entry_field.deps + |> List.fold_left( + (res: option(InputField.validated), dep: FieldDep.t) => + switch (res, dep) { + | (Some(_), _) => res + | (None, DepField(dep)) => None + | ( + None, + DepFieldOfCollection({ + collection: dep_collection, + field: dep_field, + }), + ) => + if (dep_collection.singular + == subject_collection.singular + && dep_field == subject_field.name) { + Some(ValidatedInputField(entry_field)); + } else { + None; + } + }, + None, + ) + | ( + None, + ValidatedInputFieldOfCollection({ + collection: subject_collection, + field: subject_field, + }), + ValidatedInputCollection({ + collection: entry_collection, + fields: entry_fields, + }), + ) => + entry_fields + |> List.fold_left( + ( + res: option(InputField.validated), + entry_field: InputFieldData.validated, + ) => + entry_field.deps + |> List.fold_left( + (res: option(InputField.validated), dep: FieldDep.t) => + switch (res, dep) { + | (Some(_), _) => res + | (None, DepField(dep)) => None + | ( + None, + DepFieldOfCollection({ + collection: dep_collection, + field: dep_field, + }), + ) => + if (subject_collection.singular + == dep_collection.singular + && subject_field.name == dep_field) { + Some( + ValidatedInputFieldOfCollection({ + collection: entry_collection, + field: entry_field, + }), + ); + } else { + None; + } + }, + None, + ), + None, + ) + }, + None, + ); + }; +}; + +module OutputTypeParser = { + type result = Pervasives.result(ok, error) + and ok = + | NotProvided + | AliasOfInput + | Record({ + entries: list(entry), + loc: Location.t, + }) + and entry = + | OutputField(OutputFieldData.t) + | OutputCollection({ + collection: Collection.t, + fields: list(OutputFieldData.t), + output_type: ItemType.t, + }) + and error = + | InputNotAvailable(Location.t) + | NotRecord(Location.t) + | BadTypeAlias({ + alias: string, + loc: Location.t, + }) + | OutputCollectionNotFound({ + input_collection: Collection.t, + loc: Location.t, + }) + | InvalidCollection(collection_error) + and collection_error = + | InvalidCollectionTypeRef(Location.t) + | CollectionTypeNotRecord(Location.t) + | CollectionTypeNotFound(Location.t) + | CollectionOutputNotArray(Location.t); + + let flatten = (entries: list(entry)): list(OutputField.t) => + List.fold_right( + (entry, acc) => + switch (entry) { + | OutputField(field) => [OutputField.OutputField(field), ...acc] + | OutputCollection({collection, fields}) => + List.fold_right( + (field, acc) => + [ + OutputField.OutputFieldOfCollection({collection, field}), + ...acc, + ], + fields, + acc, + ) + }, + entries, + [], + ); + + let parse = + ( + ~structure, + ~input_collections: list(Collection.t), + ~loc, + fields: list(label_declaration), + ) => + switch (input_collections) { + | [] => + Ok( + Record({ + loc, + entries: + List.fold_right( + (field, acc) => + [ + OutputField({ + name: field.pld_name.txt, + typ: field.pld_type |> ItemType.make, + loc: field.pld_loc, + }), + ...acc, + ], + fields, + [], + ), + }), + ) + | _ => + let entries = + List.fold_right( + (field, acc) => + switch (acc) { + | Error(error) => Error(error) + | Ok(entries) => + let field_name = field.pld_name.txt; + switch ( + input_collections + |> List.find_opt((collection: Collection.t) => + collection.plural == field_name + ) + ) { + | None => + Ok([ + OutputField({ + name: field_name, + typ: field.pld_type |> ItemType.make, + loc: field.pld_loc, + }), + ...entries, + ]) + | Some(input_collection) => + switch (field.pld_type.ptyp_desc) { + | Ptyp_constr({txt: Lident("array"), loc: arr_loc}, payload) => + switch (payload) { + | [] => Error(InvalidCollectionTypeRef(arr_loc)) + | [ + { + ptyp_desc: Ptyp_constr({txt: Lident(type_name)}, []), + ptyp_loc, + } as output_type, + ..._, + ] => + let record_type = ref(None); + structure + |> List.iter((item: structure_item) => + switch (item) { + | {pstr_desc: Pstr_type(rec_flag, decls)} => + decls + |> List.iter((decl: type_declaration) => + switch (decl) { + | {ptype_name: {txt: name}} + when name == type_name => + switch (decl.ptype_kind) { + | Ptype_record(fields) => + record_type := Some(Ok(fields)) + | _ => + record_type := + Some( + Error( + CollectionTypeNotRecord( + decl.ptype_loc, + ), + ), + ) + } + | _ => () + } + ) + | _ => () + } + ); + switch (record_type^) { + | None => Error(CollectionTypeNotFound(ptyp_loc)) + | Some(Error(error)) => Error(error) + | Some(Ok(fields)) => + Ok([ + OutputCollection({ + collection: { + plural: field_name, + singular: type_name, + }, + fields: + fields + |> List.map((field: label_declaration) => + OutputFieldData.{ + name: field.pld_name.txt, + typ: field.pld_type |> ItemType.make, + loc: field.pld_loc, + } + ), + output_type: output_type |> ItemType.make, + }), + ...entries, + ]) + }; + | [{ptyp_loc}, ..._] => + Error(InvalidCollectionTypeRef(ptyp_loc)) + } + | _ => Error(CollectionOutputNotArray(field.pld_loc)) + } + }; + }, + fields, + Ok([]), + ); + switch (entries) { + | Ok(entries) => Ok(Record({loc, entries})) + | Error(error) => Error(InvalidCollection(error)) + }; + }; +}; + +module DebounceIntervalParser = { + let exists = (values: list(value_binding)) => + values + |> List.exists( + fun + | {pvb_pat: {ppat_desc: Ppat_var({txt: "debounceInterval"})}} => + true + | _ => false, + ); +}; + +module ValidatorsRecordParser = { + type result = Pervasives.result(ValidatorsRecord.t, error) + and error = + | NotFound + | NotRecord(Location.t) + | BadTypeAnnotation(Location.t) + | ValidatorError( + [ + | `BadRequiredValidator( + InputField.validated, + [ | `Some(Location.t) | `None(Location.t)], + [ + | `IncludedInDeps(InputField.validated) + | `DifferentIO(ItemType.t, ItemType.t) + ], + ) + ], + ) + | RecordParseError(Location.t); + + let exists = (values: list(value_binding)) => + values + |> List.exists( + fun + | {pvb_pat: {ppat_desc: Ppat_var({txt: "validators"})}} => true + | _ => false, + ); + + let parse = (~rec_flag, values: list(value_binding)): option(result) => { + values + |> List.fold_left( + (res, value) => + switch (res) { + | Some(_) => res + | None => + switch (value) { + | { + pvb_pat: {ppat_desc: Ppat_var({txt: "validators"})}, + pvb_expr: { + pexp_desc: + Pexp_constraint( + expr, + {ptyp_desc: Ptyp_constr(typ, args), ptyp_loc} as annotation, + ), + pexp_loc: constraint_pexp_loc, + pexp_loc_stack: constraint_pexp_loc_stack, + pexp_attributes: constraint_pexp_attributes, + }, + } => + switch (typ, args) { + | ({txt: Lident("validators")}, []) => + switch (expr) { + | { + pexp_desc: Pexp_record(fields, None), + pexp_loc: record_pexp_loc, + pexp_loc_stack: record_pexp_loc_stack, + pexp_attributes: record_pexp_attributes, + } => + Some( + Ok( + ValidatorsRecord.{ + fields, + rec_flag, + annotation, + constraint_metadata: { + pexp_loc: constraint_pexp_loc, + pexp_loc_stack: constraint_pexp_loc_stack, + pexp_attributes: constraint_pexp_attributes, + }, + record_metadata: { + pexp_loc: record_pexp_loc, + pexp_loc_stack: record_pexp_loc_stack, + pexp_attributes: record_pexp_attributes, + }, + }, + ), + ) + | {pexp_loc} => Some(Error(NotRecord(pexp_loc))) + } + | ({txt: _}, _) => Some(Error(BadTypeAnnotation(ptyp_loc))) + } + | { + pvb_pat: {ppat_desc: Ppat_var({txt: "validators"})}, + pvb_expr: {pexp_loc} as expr, + } => + switch (expr) { + | { + pexp_desc: Pexp_record(fields, None), + pexp_loc: loc, + pexp_loc_stack, + pexp_attributes, + } => + Some( + Ok({ + fields, + rec_flag, + annotation: [%type: validators], + constraint_metadata: { + pexp_loc, + pexp_loc_stack, + pexp_attributes, + }, + record_metadata: { + pexp_loc, + pexp_loc_stack, + pexp_attributes, + }, + }), + ) + | {pexp_loc} => Some(Error(NotRecord(pexp_loc))) + } + | _ => None + } + }, + None, + ); + }; + + let find_field = + (field: InputField.validated, validators: ValidatorsRecord.fields) => + validators + |> List.fold_left( + (res, validator) => + switch (res, field, validator) { + | (Some(_), _, _) => res + | (None, ValidatedInputField(field), ({txt: Lident(key)}, _)) => + field.name == key ? Some(validator) : None + | ( + None, + ValidatedInputFieldOfCollection({collection, field}), + ( + {txt: Lident(key)}, + {pexp_desc: Pexp_record(fields, None)}, + ), + ) => + if (collection.plural == key) { + fields + |> List.fold_left( + (res, entry) => + switch (res, entry) { + | (Some(_), _) => res + | ( + None, + ( + {txt: Lident("fields")}, + {pexp_desc: Pexp_record(fields, None)}, + ), + ) => + fields + |> List.find_opt(entry => + switch (entry) { + | ({txt: Lident(key)}, _) => key == field.name + | _ => false + } + ) + | (None, _) => None + }, + None, + ); + } else { + None; + } + | ( + None, + ValidatedInputFieldOfCollection({collection, field}), + ({txt: _}, _), + ) => + None + | (None, ValidatedInputField(_), ({txt: _}, _)) => None + }, + None, + ); + + let find_collection = + (collection: Collection.t, validators: ValidatorsRecord.fields) => + validators + |> List.fold_left( + (res, validator) => + switch (res, validator) { + | (Some(_), _) => res + | ( + None, + ( + {txt: Lident(key)}, + {pexp_desc: Pexp_record(fields, None)}, + ), + ) + when collection.plural == key => + fields + |> List.fold_left( + (res, entry) => + switch (res, entry) { + | (Some(_), _) => res + | (None, ({txt: Lident("collection")}, exp)) => + Some(exp) + | (None, _) => None + }, + None, + ) + | (None, ({txt: _}, _)) => None + }, + None, + ); + + let required = + (field: InputField.validated, validators: ValidatorsRecord.fields) => { + switch (validators |> find_field(field)) { + | Some((_, {pexp_desc: Pexp_record(_)})) => Ok() + | Some(( + _, + { + pexp_desc: + Pexp_construct( + {txt: Lident("Some")}, + Some({pexp_desc: Pexp_record(_)}), + ), + pexp_loc, + }, + )) => + Error(`Some(pexp_loc)) + | Some(( + _, + {pexp_desc: Pexp_construct({txt: Lident("None")}, None), pexp_loc}, + )) => + Error(`None(pexp_loc)) + | Some(_) => Error(`BadValue) + | None => Error(`NotFound) + }; + }; + + let optional = + (field: InputField.validated, validators: ValidatorsRecord.fields) => { + switch (validators |> find_field(field)) { + | Some((_, {pexp_desc: Pexp_record(_)})) => Ok(Some()) + | Some((_, {pexp_desc: Pexp_construct({txt: Lident("None")}, None)})) => + Ok(None) + | Some(_) => Error(`BadValue) + | None => Error(`NotFound) + }; + }; + + let collection = + (collection: Collection.t, validators: ValidatorsRecord.fields) => + switch (validators |> find_collection(collection)) { + | Some({pexp_desc: Pexp_fun(_)}) => Ok(Some()) + | Some({pexp_desc: Pexp_construct({txt: Lident("None")}, None)}) => + Ok(None) + | Some(_) => Error(`BadValue) + | None => Error(`NotFound) + }; +}; + +module Metadata = { + type t = { + scheme: Scheme.t, + async: bool, // meh, it should be variant: Sync(_) | Async(_) + output_type: OutputTypeParser.ok, + validators_record: ValidatorsRecord.t, + message_type: option(unit), + submission_error_type: option(unit), + debounce_interval: option(unit), + }; + + type error = + | InputTypeParseError(InputTypeParser.error) + | OutputTypeParseError(OutputTypeParser.error) + | ValidatorsRecordParseError(ValidatorsRecordParser.error) + | IOMismatch(io_mismatch) + and io_mismatch = + | InputFieldsNotInOutput({ + fields: list(InputField.validated), + loc: Location.t, + }) + | OutputFieldsNotInInput({fields: list(OutputField.t)}) + | Both({ + input_fields_not_in_output: list(InputField.validated), + output_fields_not_in_input: list(OutputField.t), + loc: Location.t, + }); + + let make = (structure: structure) => { + let input_parsing_result: ref(option(InputTypeParser.result)) = + ref(None); + let output_parsing_result: ref(OutputTypeParser.result) = + ref(Ok(OutputTypeParser.NotProvided)); + let validators_record_parsing_result: + ref(option(ValidatorsRecordParser.result)) = + ref(None); + let message_type: ref(option(unit)) = ref(None); + let submission_error_type: ref(option(unit)) = ref(None); + let debounce_interval_value: ref(option(unit)) = ref(None); + + structure + |> List.iter( + fun + | {pstr_desc: Pstr_type(rec_flag, decls)} => { + decls + |> List.iter( + fun + // Input type + | { + ptype_name: {txt: "input"}, + ptype_kind: Ptype_record(fields), + ptype_loc, + } as decl => + input_parsing_result := + Some( + fields + |> InputTypeParser.parse( + ~decl, + ~structure, + ~loc=ptype_loc, + ), + ) + | {ptype_name: {txt: "input"}, ptype_loc} => + input_parsing_result := + Some(Error(InputTypeParser.NotRecord(ptype_loc))) + + // Output type + | { + ptype_name: {txt: "output"}, + ptype_kind: Ptype_record(fields), + ptype_loc, + } => + switch (input_parsing_result^) { + | None => + output_parsing_result := + Error(InputNotAvailable(ptype_loc)) + | Some(Ok({entries})) => + output_parsing_result := + fields + |> OutputTypeParser.parse( + ~structure, + ~loc=ptype_loc, + ~input_collections= + entries + |> List.fold_left( + ( + acc, + entry: InputTypeParser.unvalidated_entry, + ) => + switch (entry) { + | UnvalidatedInputField(_) => acc + | UnvalidatedInputCollection({ + collection, + }) => [ + collection, + ...acc, + ] + }, + [], + ), + ) + | Some(Error(_)) => () + } + | { + ptype_name: {txt: "output"}, + ptype_kind: Ptype_abstract, + ptype_loc, + ptype_manifest: + Some({ + ptyp_desc: + Ptyp_constr({txt: Lident("input")}, []), + }), + } => + output_parsing_result := Ok(AliasOfInput) + | { + ptype_name: {txt: "output"}, + ptype_kind: Ptype_abstract, + ptype_manifest: + Some({ + ptyp_desc: + Ptyp_constr({txt: Lident(alias), loc}, []), + }), + } => + output_parsing_result := + Error(OutputTypeParser.BadTypeAlias({alias, loc})) + | {ptype_name: {txt: "output"}, ptype_loc} => + output_parsing_result := + Error(OutputTypeParser.NotRecord(ptype_loc)) + + // Message type + | {ptype_name: {txt: "message"}, ptype_loc} => + message_type := Some() + + // Submission error type + | {ptype_name: {txt: "submissionError"}, ptype_loc} => + submission_error_type := Some() + + // Rest + | _ => (), + ); + } + | {pstr_desc: Pstr_value(rec_flag, values)} => { + if (values |> DebounceIntervalParser.exists) { + debounce_interval_value := Some(); + }; + switch (values |> ValidatorsRecordParser.parse(~rec_flag)) { + | Some(x) => validators_record_parsing_result := Some(x) + | None => () + }; + } + | _ => (), + ); + + switch ( + input_parsing_result^, + output_parsing_result^, + validators_record_parsing_result^, + ) { + | (Some(Error(error)), _, _) => Error(InputTypeParseError(error)) + | (None, _, _) => Error(InputTypeParseError(NotFound)) + | (_, Error(error), _) => Error(OutputTypeParseError(error)) + | (_, _, None) => Error(ValidatorsRecordParseError(NotFound)) + | (_, _, Some(Error(error))) => + Error(ValidatorsRecordParseError(error)) + | ( + Some(Ok(input_data)), + Ok(output_result), + Some(Ok(validators_record)), + ) => + switch (input_data.entries |> InputTypeParser.validate) { + | Error(error) => + Error( + InputTypeParseError(InvalidAttributes(InvalidFieldDeps(error))), + ) + | Ok(validated_input_entries) => + let scheme: result(Scheme.t, error) = + switch (output_result) { + | NotProvided + | AliasOfInput => + let validator = + ( + ~field: InputField.validated, + ~entries: list(InputTypeParser.validated_entry), + ~validators_record: ValidatorsRecord.t, + ~async_mode: option(AsyncMode.t), + ~output_type: ItemType.t, + ) + : result(FieldValidator.t, error) => + switch (async_mode) { + | None => + switch (field |> InputTypeParser.in_deps_of(entries)) { + | Some(in_deps_of_entry) => + switch ( + validators_record.fields + |> ValidatorsRecordParser.required(field) + ) { + | Ok () => Ok(SyncValidator(Ok(Required))) + | Error(`NotFound | `BadValue) => + // Proceeding here since compiler + // would give more insightful error message + Ok(SyncValidator(Error())) + | Error(`Some(_) as reason | `None(_) as reason) => + // In this case we can give more insights (hopefully) + // on how to fix this error + Error( + ValidatorsRecordParseError( + ValidatorError( + `BadRequiredValidator(( + field, + reason, + `IncludedInDeps(in_deps_of_entry), + )), + ), + ), + ) + } + | None => + switch ( + validators_record.fields + |> ValidatorsRecordParser.optional(field) + ) { + | Ok(res) => Ok(SyncValidator(Ok(Optional(res)))) + | Error(`NotFound | `BadValue) => + // Proceeding here since compiler + // would give more insightful error message + Ok(SyncValidator(Error())) + } + } + | Some(mode) => + Ok( + AsyncValidator({ + mode, + optionality: output_type |> FieldOptionalityParser.parse, + }), + ) + }; + + validated_input_entries + |> List.fold_left( + (res, entry: InputTypeParser.validated_entry) => { + switch (res, entry) { + | (Error(error), _) => Error(error) + | (Ok(scheme), ValidatedInputField(field)) => + let validator = + validator( + ~field=ValidatedInputField(field), + ~entries=validated_input_entries, + ~validators_record, + ~async_mode=field.async, + ~output_type=field.typ, + ); + switch (validator) { + | Ok(validator) => + Ok([ + Scheme.Field({ + name: field.name, + input_type: field.typ, + output_type: field.typ, + validator, + deps: field.deps, + }), + ...scheme, + ]) + | Error(error) => Error(error) + }; + + | ( + Ok(scheme), + ValidatedInputCollection({ + collection, + fields, + input_type, + }), + ) => + let fields = + fields + |> List.fold_left( + (res, field) => + switch (res) { + | Error(error) => Error(error) + | Ok(fields) => + let validator = + validator( + ~field= + ValidatedInputFieldOfCollection({ + collection, + field, + }), + ~entries=validated_input_entries, + ~validators_record, + ~async_mode=field.async, + ~output_type=field.typ, + ); + switch (validator) { + | Ok(validator) => + Ok([ + Scheme.{ + name: field.name, + input_type: field.typ, + output_type: field.typ, + validator, + deps: field.deps, + }, + ...fields, + ]) + | Error(error) => Error(error) + }; + }, + Ok([]), + ); + switch (fields) { + | Error(error) => Error(error) + | Ok(fields) => + Ok([ + Scheme.Collection({ + collection, + fields, + input_type, + output_type: input_type, + validator: + switch ( + validators_record.fields + |> ValidatorsRecordParser.collection( + collection, + ) + ) { + | Ok(res) => Ok(res) + | Error(_) => Error() + }, + }), + ...scheme, + ]) + }; + } + }, + Ok([]), + ); + | Record({entries: output_entries, loc: output_loc}) => + let validator = + ( + ~input_field: InputField.validated, + ~input_field_data: InputFieldData.validated, + ~output_field_data: OutputFieldData.t, + ~input_entries: list(InputTypeParser.validated_entry), + ~validators_record: ValidatorsRecord.t, + ) + : result(FieldValidator.t, error) => + switch (input_field_data.async) { + | None => + switch ( + input_field |> InputTypeParser.in_deps_of(input_entries) + ) { + | Some(in_deps_of_field) => + switch ( + validators_record.fields + |> ValidatorsRecordParser.required(input_field) + ) { + | Ok () => Ok(SyncValidator(Ok(Required))) + | Error(`NotFound | `BadValue) => + // Proceeding here since compiler + // would give more insightful error message + Ok(SyncValidator(Error())) + | Error(`Some(_) as reason | `None(_) as reason) => + // In this case we can give more insights (hopefully) + // on how to fix this error + Error( + ValidatorsRecordParseError( + ValidatorError( + `BadRequiredValidator(( + input_field, + reason, + `IncludedInDeps(in_deps_of_field), + )), + ), + ), + ) + } + | None => + if (ItemType.eq(input_field_data.typ, output_field_data.typ)) { + switch ( + validators_record.fields + |> ValidatorsRecordParser.optional(input_field) + ) { + | Ok(res) => Ok(SyncValidator(Ok(Optional(res)))) + | Error(`NotFound | `BadValue) => + // Proceeding here since compiler + // would give more insightful error message + Ok(SyncValidator(Error())) + }; + } else { + switch ( + validators_record.fields + |> ValidatorsRecordParser.required(input_field) + ) { + | Ok () => Ok(SyncValidator(Ok(Required))) + | Error(`NotFound | `BadValue) => + // Proceeding here since compiler + // would give more insightful error message + Ok(SyncValidator(Error())) + | Error(`Some(_) as reason | `None(_) as reason) => + // In this case we can give more insights (hopefully) + // on how to fix this error + Error( + ValidatorsRecordParseError( + ValidatorError( + `BadRequiredValidator(( + input_field, + reason, + `DifferentIO(( + input_field_data.typ, + output_field_data.typ, + )), + )), + ), + ), + ) + }; + } + } + | Some(mode) => + Ok( + AsyncValidator({ + mode, + optionality: + output_field_data.typ |> FieldOptionalityParser.parse, + }), + ) + }; + + let ( + result, + input_fields_not_in_output, + output_fields_not_in_input, + ) = + List.fold_right( + ( + input_entry: InputTypeParser.validated_entry, + ( + result: result(Scheme.t, error), + input_fields_not_in_output: list(InputField.validated), + output_fields_not_in_input: list(OutputField.t), + ), + ) => + switch (input_entry) { + | ValidatedInputField(input_field_data) => + let output_field_data = + output_entries + |> List.fold_left( + (res, output_entry: OutputTypeParser.entry) => + switch (res, output_entry) { + | (Some(_), _) => res + | (None, OutputField(output_field_data)) => + input_field_data.name == output_field_data.name + ? Some(output_field_data) : None + | (None, OutputCollection(_)) => None + }, + None, + ); + switch (result, output_field_data) { + | (_, None) => ( + result, + [ + ValidatedInputField(input_field_data), + ...input_fields_not_in_output, + ], + output_fields_not_in_input, + ) + + | (Error(error), Some(output_field_data)) => ( + Error(error), + input_fields_not_in_output, + output_fields_not_in_input + |> List.filter((output_field: OutputField.t) => + switch (output_field) { + | OutputField(output_field_data) => + output_field_data.name != input_field_data.name + | OutputFieldOfCollection(_) => true + } + ), + ) + + | (Ok(scheme), Some(output_field_data)) => + let validator = + validator( + ~input_field=ValidatedInputField(input_field_data), + ~input_field_data, + ~output_field_data, + ~input_entries=validated_input_entries, + ~validators_record, + ); + ( + switch (validator) { + | Error(error) => Error(error) + | Ok(validator) => + Ok([ + Scheme.Field({ + name: input_field_data.name, + input_type: input_field_data.typ, + output_type: output_field_data.typ, + validator, + deps: input_field_data.deps, + }), + ...scheme, + ]) + }, + input_fields_not_in_output, + output_fields_not_in_input + |> List.filter((output_field: OutputField.t) => + switch (output_field) { + | OutputField(output_field_data) => + output_field_data.name != input_field_data.name + | OutputFieldOfCollection(_) => true + } + ), + ); + }; + + | ValidatedInputCollection({ + collection: input_collection, + fields: input_fields, + input_type: input_collection_type, + }) => + let output_collection = + output_entries + |> List.fold_left( + (res, output_entry: OutputTypeParser.entry) => + switch (res, output_entry) { + | (Some(_), _) => res + | (None, OutputField(_)) => res + | ( + None, + OutputCollection({ + collection: output_collection, + fields, + output_type, + }), + ) => + if (output_collection.plural + == input_collection.plural) { + Some(( + output_collection, + fields, + output_type, + )); + } else { + None; + } + }, + None, + ); + switch (output_collection) { + | None => ( + Error( + OutputTypeParseError( + OutputCollectionNotFound({ + input_collection, + loc: output_loc, + }), + ), + ), + input_fields + |> List.fold_left( + (acc: list(InputField.validated), field) => + [ + ValidatedInputFieldOfCollection({ + collection: input_collection, + field, + }), + ...acc, + ], + input_fields_not_in_output, + ), + output_fields_not_in_input, + ) + | Some((output_collection, output_fields, output_type)) => + let ( + fields, + input_fields_not_in_output, + output_fields_not_in_input, + ) = + List.fold_right( + ( + input_field_data: InputFieldData.validated, + ( + res: result(list(Scheme.field), error), + input_fields_not_in_output: + list(InputField.validated), + output_fields_not_in_input: list(OutputField.t), + ), + ) => { + let output_field_data = + output_fields + |> List.find_opt( + (output_field_data: OutputFieldData.t) => + output_field_data.name + == input_field_data.name + ); + + switch (res, output_field_data) { + | (_, None) => ( + res, + [ + ValidatedInputFieldOfCollection({ + collection: input_collection, + field: input_field_data, + }), + ...input_fields_not_in_output, + ], + output_fields_not_in_input, + ) + + | (Error(error), Some(output_field_data)) => ( + Error(error), + input_fields_not_in_output, + output_fields_not_in_input + |> List.filter((output_field: OutputField.t) => + switch (output_field) { + | OutputField(_) => true + | OutputFieldOfCollection({ + collection, + field, + }) => + !( + input_collection.plural + == collection.plural + && output_field_data.name + == field.name + ) + } + ), + ) + | (Ok(fields), Some(output_field_data)) => + let validator = + validator( + ~input_field= + ValidatedInputFieldOfCollection({ + collection: input_collection, + field: input_field_data, + }), + ~input_field_data, + ~output_field_data, + ~input_entries=validated_input_entries, + ~validators_record, + ); + ( + switch (validator) { + | Error(error) => Error(error) + | Ok(validator) => + Ok([ + { + name: input_field_data.name, + input_type: input_field_data.typ, + output_type: output_field_data.typ, + validator, + deps: input_field_data.deps, + }, + ...fields, + ]) + }, + input_fields_not_in_output, + output_fields_not_in_input + |> List.filter((output_field: OutputField.t) => + switch (output_field) { + | OutputField(_) => true + | OutputFieldOfCollection({ + collection, + field, + }) => + !( + input_collection.plural + == collection.plural + && output_field_data.name + == field.name + ) + } + ), + ); + }; + }, + input_fields, + ( + Ok([]), + input_fields_not_in_output, + output_fields_not_in_input, + ), + ); + + switch (result, fields) { + | (Error(error), _) => ( + result, + input_fields_not_in_output, + output_fields_not_in_input, + ) + | (Ok(_), Error(error)) => ( + Error(error), + input_fields_not_in_output, + output_fields_not_in_input, + ) + | (Ok(scheme), Ok(fields)) => ( + Ok([ + Scheme.Collection({ + collection: input_collection, + fields, + input_type: input_collection_type, + output_type, + validator: + switch ( + validators_record.fields + |> ValidatorsRecordParser.collection( + input_collection, + ) + ) { + | Ok(res) => Ok(res) + | Error(_) => Error() + }, + }), + ...scheme, + ]), + input_fields_not_in_output, + output_fields_not_in_input, + ) + }; + }; + }, + validated_input_entries, + (Ok([]), [], output_entries |> OutputTypeParser.flatten), + ); + switch (input_fields_not_in_output, output_fields_not_in_input) { + | ([], []) => result + | (input_fields_not_in_output, []) => + Error( + IOMismatch( + InputFieldsNotInOutput({ + fields: input_fields_not_in_output, + loc: output_loc, + }), + ), + ) + | ([], output_entries_not_in_input) => + Error( + IOMismatch( + OutputFieldsNotInInput({ + fields: output_fields_not_in_input, + }), + ), + ) + | (input_fields_not_in_output, output_fields_not_in_input) => + Error( + IOMismatch( + Both({ + input_fields_not_in_output, + output_fields_not_in_input, + loc: output_loc, + }), + ), + ) + }; + }; + + switch (scheme) { + | Ok(scheme) => + Ok({ + scheme, + async: + // TODO: Quick and dirty. + // Scheme.t should be wrapped in variant instead, probably. + // Let's do base implementation first, + // then look into how to redesign it better + scheme + |> List.exists((entry: Scheme.entry) => + switch (entry) { + | Field({validator: AsyncValidator(_)}) => true + | Field({validator: SyncValidator(_)}) => false + | Collection({fields}) => + fields + |> List.exists((field: Scheme.field) => + switch (field) { + | {validator: AsyncValidator(_)} => true + | {validator: SyncValidator(_)} => false + } + ) + } + ), + output_type: output_result, + validators_record, + message_type: message_type^, + submission_error_type: submission_error_type^, + debounce_interval: debounce_interval_value^, + }) + | Error(error) => Error(error) + }; + } + }; + }; +}; diff --git a/lib/ppx/Ppx.re b/lib/ppx/Ppx.re new file mode 100644 index 00000000..cb8f380b --- /dev/null +++ b/lib/ppx/Ppx.re @@ -0,0 +1 @@ +"formality" |> Ppxlib.Driver.register_transformation(~extensions=[Form.ext]); diff --git a/lib/ppx/Printer.re b/lib/ppx/Printer.re new file mode 100644 index 00000000..d10b34f8 --- /dev/null +++ b/lib/ppx/Printer.re @@ -0,0 +1,74 @@ +open Meta; + +module FieldPrinter = { + let update_action = (~field) => + "Update" ++ (field |> String.capitalize_ascii) ++ "Field"; + + let blur_action = (~field) => + "Blur" ++ (field |> String.capitalize_ascii) ++ "Field"; + + let apply_async_result_action = (~field) => + "ApplyAsyncResultFor" ++ (field |> String.capitalize_ascii) ++ "Field"; + + let update_fn = (~field) => "update" ++ (field |> String.capitalize_ascii); + + let blur_fn = (~field) => "blur" ++ (field |> String.capitalize_ascii); + + let result_value = (~field) => field ++ "Result"; +}; + +module FieldOfCollectionPrinter = { + let update_action = (~field, ~collection: Collection.t) => + "Update" + ++ (collection.singular |> String.capitalize_ascii) + ++ (field |> String.capitalize_ascii) + ++ "Field"; + + let blur_action = (~field, ~collection: Collection.t) => + "Blur" + ++ (collection.singular |> String.capitalize_ascii) + ++ (field |> String.capitalize_ascii) + ++ "Field"; + + let apply_async_result_action = (~field, ~collection: Collection.t) => + "ApplyAsyncResultFor" + ++ (collection.singular |> String.capitalize_ascii) + ++ (field |> String.capitalize_ascii) + ++ "Field"; + + let update_fn = (~field, ~collection: Collection.t) => + "update" + ++ (collection.singular |> String.capitalize_ascii) + ++ (field |> String.capitalize_ascii); + + let blur_fn = (~field, ~collection: Collection.t) => + "blur" + ++ (collection.singular |> String.capitalize_ascii) + ++ (field |> String.capitalize_ascii); + + let result_fn = (~field, ~collection: Collection.t) => + collection.singular ++ (field |> String.capitalize_ascii) ++ "Result"; +}; + +module CollectionPrinter = { + let fields_statuses_type = (collection: Collection.t) => + collection.singular ++ "FieldsStatuses"; + + let validator_type = (collection: Collection.t) => + collection.plural ++ "Validators"; + + let add_action = (collection: Collection.t) => + "Add" ++ (collection.singular |> String.capitalize_ascii) ++ "Entry"; + + let remove_action = (collection: Collection.t) => + "Remove" ++ (collection.singular |> String.capitalize_ascii) ++ "Entry"; + + let add_fn = (collection: Collection.t) => + "add" ++ (collection.singular |> String.capitalize_ascii); + + let remove_fn = (collection: Collection.t) => + "remove" ++ (collection.singular |> String.capitalize_ascii); + + let result_value = (collection: Collection.t) => + collection.plural ++ "Result"; +}; diff --git a/lib/ppx/dune b/lib/ppx/dune new file mode 100644 index 00000000..8192b5f0 --- /dev/null +++ b/lib/ppx/dune @@ -0,0 +1,8 @@ +(library + (name Ppx) + (public_name re-formality-ppx.lib) + (kind ppx_rewriter) + (libraries ppxlib) + (flags (:standard -w -30)) + (preprocess (pps ppxlib.metaquot ppxlib.runner)) +) diff --git a/lib/re-formality-ppx.opam b/lib/re-formality-ppx.opam new file mode 100644 index 00000000..e69de29b diff --git a/lib/sandbox/README.md b/lib/sandbox/README.md new file mode 100644 index 00000000..886efbb8 --- /dev/null +++ b/lib/sandbox/README.md @@ -0,0 +1,9 @@ +# Sandbox + +Used as a sandbox for conveniently debugging test cases. + +Run the script and debug generated code of the test case using editor tooling: + +```shell +test/script/sandbox [TEST_CASE] +``` diff --git a/lib/sandbox/bsconfig.json b/lib/sandbox/bsconfig.json new file mode 100644 index 00000000..0df9709d --- /dev/null +++ b/lib/sandbox/bsconfig.json @@ -0,0 +1,20 @@ +{ + "name": "re-formality-sandbox", + "sources": ["src"], + "bs-dependencies": [ + "reason-react", + "re-formality" + ], + "reason": { + "react-jsx": 3 + }, + "refmt": 3, + "package-specs": { + "module": "es6", + "in-source": true + }, + "suffix": ".bs.js", + "warnings": { + "number": "+A" + } +} diff --git a/lib/sandbox/package.json b/lib/sandbox/package.json new file mode 100644 index 00000000..84a70e33 --- /dev/null +++ b/lib/sandbox/package.json @@ -0,0 +1,13 @@ +{ + "name": "re-formality-sandbox", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "bsb -clean-world -make-world -w" + }, + "dependencies": { + "bs-platform": "7.2.2", + "re-formality": "*", + "reason-react": "0.7.0" + } +} diff --git a/lib/sandbox/src/Sandbox.re b/lib/sandbox/src/Sandbox.re new file mode 100644 index 00000000..e69de29b diff --git a/lib/src/Formality.re b/lib/src/Formality.re new file mode 100644 index 00000000..47e0f2af --- /dev/null +++ b/lib/src/Formality.re @@ -0,0 +1,1107 @@ +module Debouncer = Formality__Debouncer; +module ReactUpdate = Formality__ReactUpdate; + +type strategy = + | OnFirstBlur + | OnFirstChange + | OnFirstSuccess + | OnFirstSuccessOrFirstBlur + | OnSubmit; + +type visibility = + | Shown + | Hidden; + +type fieldStatus('outputValue, 'message) = + | Pristine + | Dirty(result('outputValue, 'message), visibility); + +type collectionStatus('message) = result(unit, 'message); + +type formStatus('submissionError) = + | Editing + | Submitting(option('submissionError)) + | Submitted + | SubmissionFailed('submissionError); + +type submissionStatus = + | NeverSubmitted + | AttemptedToSubmit; + +let exposeFieldResult = + (fieldStatus: fieldStatus('outputValue, 'message)) + : option(result('outputValue, 'message)) => + switch (fieldStatus) { + | Pristine + | Dirty(_, Hidden) => None + | Dirty(result, Shown) => Some(result) + }; + +type index = int; + +type singleValueValidator('input, 'outputValue, 'message) = { + strategy, + validate: 'input => result('outputValue, 'message), +}; + +type collectionValidatorWithWholeCollectionValidator( + 'input, + 'message, + 'fieldsValidators, +) = { + collection: 'input => result(unit, 'message), + fields: 'fieldsValidators, +}; + +type collectionValidatorWithoutWholeCollectionValidator('fieldsValidators) = { + collection: unit, + fields: 'fieldsValidators, +}; + +type valueOfCollectionValidator('input, 'outputValue, 'message) = { + strategy, + validate: ('input, ~at: index) => result('outputValue, 'message), +}; + +type formValidationResult('output, 'fieldsStatuses, 'collectionsStatuses) = + | Valid({ + output: 'output, + fieldsStatuses: 'fieldsStatuses, + collectionsStatuses: 'collectionsStatuses, + }) + | Invalid({ + fieldsStatuses: 'fieldsStatuses, + collectionsStatuses: 'collectionsStatuses, + }); + +type submissionCallbacks('input, 'submissionError) = { + notifyOnSuccess: option('input) => unit, + notifyOnFailure: 'submissionError => unit, + reset: unit => unit, + dismissSubmissionResult: unit => unit, +}; + +let validateFieldOnChangeWithoutValidator = + ( + ~fieldInput: 'outputValue, + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : 'statuses => { + Dirty(Ok(fieldInput), Hidden)->setStatus; +}; + +let validateFieldOnChangeWithValidator = + ( + ~input: 'input, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~submissionStatus: submissionStatus, + ~validator: singleValueValidator('input, 'outputValue, 'message), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + Dirty(validator.validate(input), Shown)->setStatus + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok(_) as result => Dirty(result, Shown)->setStatus + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input), Hidden)->setStatus + }; +}; + +let validateFieldOfCollectionOnChangeWithValidator = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~submissionStatus: submissionStatus, + ~validator: valueOfCollectionValidator('input, 'outputValue, 'message), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + Dirty(validator.validate(input, ~at=index), Shown)->setStatus + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok(_) as result => Dirty(result, Shown)->setStatus + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus + }; +}; + +let validateDependentFieldOnChange = + ( + ~input: 'input, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~validator: singleValueValidator('input, 'outputValue, 'message), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Pristine + | Dirty(_, Hidden) => None + | Dirty(_, Shown) => + Dirty(validator.validate(input), Shown)->setStatus->Some + }; +}; + +let validateDependentFieldOfCollectionOnChange = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~validator: valueOfCollectionValidator('input, 'outputValue, 'message), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Pristine + | Dirty(_, Hidden) => None + | Dirty(_, Shown) => + Dirty(validator.validate(input, ~at=index), Shown)->setStatus->Some + }; +}; + +let validateFieldOnBlurWithoutValidator = + ( + ~fieldInput: 'outputValue, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : option('statuses) => + switch (fieldStatus) { + | Dirty(_, Shown | Hidden) => None + | Pristine => Dirty(Ok(fieldInput), Hidden)->setStatus->Some + }; + +let validateFieldOnBlurWithValidator = + ( + ~input: 'input, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~validator: singleValueValidator('input, 'outputValue, 'message), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Dirty(_, Shown) => None + | Pristine + | Dirty(_, Hidden) => + switch (validator.strategy) { + | OnFirstChange + | OnFirstSuccess + | OnSubmit => Dirty(validator.validate(input), Hidden)->setStatus->Some + | OnFirstBlur + | OnFirstSuccessOrFirstBlur => + Dirty(validator.validate(input), Shown)->setStatus->Some + } + }; +}; + +let validateFieldOfCollectionOnBlurWithValidator = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~validator: valueOfCollectionValidator('input, 'outputValue, 'message), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Dirty(_, Shown) => None + | Pristine + | Dirty(_, Hidden) => + switch (validator.strategy) { + | OnFirstChange + | OnFirstSuccess + | OnSubmit => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus->Some + | OnFirstBlur + | OnFirstSuccessOrFirstBlur => + Dirty(validator.validate(input, ~at=index), Shown)->setStatus->Some + } + }; +}; + +module Async = { + type fieldStatus('outputValue, 'message) = + | Pristine + | Dirty(result('outputValue, 'message), visibility) + | Validating('outputValue); + + type exposedFieldStatus('outputValue, 'message) = + | Validating('outputValue) + | Result(result('outputValue, 'message)); + + type singleValueValidator('input, 'outputValue, 'message, 'action) = { + strategy, + validate: 'input => result('outputValue, 'message), + validateAsync: (('outputValue, 'action => unit)) => unit, + eq: ('outputValue, 'outputValue) => bool, + }; + + type valueOfCollectionValidator('input, 'outputValue, 'message, 'action) = { + strategy, + validate: ('input, ~at: index) => result('outputValue, 'message), + validateAsync: (('outputValue, index, 'action => unit)) => unit, + eq: ('outputValue, 'outputValue) => bool, + }; + + type validateAsyncFn('outputValue, 'message) = + 'outputValue => Js.Promise.t(result('outputValue, 'message)); + + let validateAsync = + ( + ~value: 'outputValue, + ~validate: validateAsyncFn('outputValue, 'message), + ~andThen: result('outputValue, 'message) => unit, + ) + : unit => + validate(value) + ->Js.Promise.(then_(res => res->andThen->resolve, _)) + ->ignore; + + type formValidationResult('output, 'fieldsStatuses, 'collectionsStatuses) = + | Valid({ + output: 'output, + fieldsStatuses: 'fieldsStatuses, + collectionsStatuses: 'collectionsStatuses, + }) + | Invalid({ + fieldsStatuses: 'fieldsStatuses, + collectionsStatuses: 'collectionsStatuses, + }) + | Validating({ + fieldsStatuses: 'fieldsStatuses, + collectionsStatuses: 'collectionsStatuses, + }); + + let exposeFieldResult = + (fieldStatus: fieldStatus('outputValue, 'message)) + : option(exposedFieldStatus('outputValue, 'message)) => + switch (fieldStatus) { + | Pristine + | Dirty(_, Hidden) => None + | Validating(x) => Some(Validating(x)) + | Dirty(result, Shown) => Some(Result(result)) + }; + + let validateFieldOnChangeInOnBlurMode = + ( + ~input: 'input, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~submissionStatus: submissionStatus, + ~validator: + singleValueValidator('input, 'outputValue, 'message, 'action), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok(_) as result => Dirty(result, Hidden)->setStatus + | Error(_) as result => Dirty(result, Shown)->setStatus + } + | ( + OnFirstSuccess | OnFirstSuccessOrFirstBlur | OnFirstBlur | OnSubmit, + _, + NeverSubmitted, + ) => + Dirty(validator.validate(input), Hidden)->setStatus + }; + }; + + let validateFieldOfCollectionOnChangeInOnBlurMode = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~submissionStatus: submissionStatus, + ~validator: + valueOfCollectionValidator( + 'input, + 'outputValue, + 'message, + 'action, + ), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok(_) as result => Dirty(result, Hidden)->setStatus + | Error(_) as result => Dirty(result, Shown)->setStatus + } + | ( + OnFirstSuccess | OnFirstSuccessOrFirstBlur | OnFirstBlur | OnSubmit, + _, + NeverSubmitted, + ) => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus + }; + }; + + let validateFieldOfOptionTypeOnChangeInOnBlurMode = + ( + ~input: 'input, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~submissionStatus: submissionStatus, + ~validator: + singleValueValidator('input, 'outputValue, 'message, 'action), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok(Some(_)) as result => Dirty(result, Hidden)->setStatus + | Ok(None) as result + | Error(_) as result => Dirty(result, Shown)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok(None) as result => Dirty(result, Shown)->setStatus + | Ok(Some(_)) as result + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input), Hidden)->setStatus + }; + }; + + let validateFieldOfCollectionOfOptionTypeOnChangeInOnBlurMode = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~submissionStatus: submissionStatus, + ~validator: + valueOfCollectionValidator( + 'input, + 'outputValue, + 'message, + 'action, + ), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok(Some(_)) as result => Dirty(result, Hidden)->setStatus + | Ok(None) as result + | Error(_) as result => Dirty(result, Shown)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok(None) as result => Dirty(result, Shown)->setStatus + | Ok(Some(_)) as result + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus + }; + }; + + let validateFieldOfStringTypeOnChangeInOnBlurMode = + ( + ~input: 'input, + ~fieldStatus: fieldStatus(string, 'message), + ~submissionStatus: submissionStatus, + ~validator: singleValueValidator('input, string, 'message, 'action), + ~setStatus: fieldStatus(string, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok("") as result + | Error(_) as result => Dirty(result, Shown)->setStatus + | Ok(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok("") as result => Dirty(result, Shown)->setStatus + | Ok(_) as result + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input), Hidden)->setStatus + }; + }; + + let validateFieldOfCollectionOfStringTypeOnChangeInOnBlurMode = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus(string, 'message), + ~submissionStatus: submissionStatus, + ~validator: + valueOfCollectionValidator('input, string, 'message, 'action), + ~setStatus: fieldStatus(string, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok("") as result + | Error(_) as result => Dirty(result, Shown)->setStatus + | Ok(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok("") as result => Dirty(result, Shown)->setStatus + | Ok(_) as result + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus + }; + }; + + let validateFieldOfOptionStringTypeOnChangeInOnBlurMode = + ( + ~input: 'input, + ~fieldStatus: fieldStatus(option(string), 'message), + ~submissionStatus: submissionStatus, + ~validator: + singleValueValidator('input, option(string), 'message, 'action), + ~setStatus: fieldStatus(option(string), 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok(Some("")) as result + | Ok(None) as result + | Error(_) as result => Dirty(result, Shown)->setStatus + | Ok(Some(_)) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok(Some("")) as result + | Ok(None) as result => Dirty(result, Shown)->setStatus + | Ok(Some(_)) as result + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input), Hidden)->setStatus + }; + }; + + let validateFieldOfCollectionOfOptionStringTypeOnChangeInOnBlurMode = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus(option(string), 'message), + ~submissionStatus: submissionStatus, + ~validator: + valueOfCollectionValidator( + 'input, + option(string), + 'message, + 'action, + ), + ~setStatus: fieldStatus(option(string), 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok(Some("")) as result + | Ok(None) as result + | Error(_) as result => Dirty(result, Shown)->setStatus + | Ok(Some(_)) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok(Some("")) as result + | Ok(None) as result => Dirty(result, Shown)->setStatus + | Ok(Some(_)) as result + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus + }; + }; + + let validateFieldOnChangeInOnChangeMode = + ( + ~input: 'input, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~submissionStatus: submissionStatus, + ~validator: + singleValueValidator('input, 'outputValue, 'message, 'action), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok(x) => Validating(x)->setStatus + | Error(_) as result => Dirty(result, Shown)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok(x) => Validating(x)->setStatus + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input), Hidden)->setStatus + }; + }; + + let validateFieldOfCollectionOnChangeInOnChangeMode = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~submissionStatus: submissionStatus, + ~validator: + valueOfCollectionValidator( + 'input, + 'outputValue, + 'message, + 'action, + ), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok(x) => Validating(x)->setStatus + | Error(_) as result => Dirty(result, Shown)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok(x) => Validating(x)->setStatus + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus + }; + }; + + let validateFieldOfOptionTypeOnChangeInOnChangeMode = + ( + ~input: 'input, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~submissionStatus: submissionStatus, + ~validator: + singleValueValidator('input, 'outputValue, 'message, 'action), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok(Some(_) as x) => Validating(x)->setStatus + | Ok(None) as result + | Error(_) as result => Dirty(result, Shown)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok(Some(_) as x) => Validating(x)->setStatus + | Ok(None) as result => Dirty(result, Shown)->setStatus + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input), Hidden)->setStatus + }; + }; + + let validateFieldOfCollectionOfOptionTypeOnChangeInOnChangeMode = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~submissionStatus: submissionStatus, + ~validator: + valueOfCollectionValidator( + 'input, + 'outputValue, + 'message, + 'action, + ), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok(Some(_) as x) => Validating(x)->setStatus + | Ok(None) as result + | Error(_) as result => Dirty(result, Shown)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok(Some(_) as x) => Validating(x)->setStatus + | Ok(None) as result => Dirty(result, Shown)->setStatus + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus + }; + }; + + let validateFieldOfStringTypeOnChangeInOnChangeMode = + ( + ~input: 'input, + ~fieldStatus: fieldStatus(string, 'message), + ~submissionStatus: submissionStatus, + ~validator: singleValueValidator('input, string, 'message, 'action), + ~setStatus: fieldStatus(string, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok("") as result + | Error(_) as result => Dirty(result, Shown)->setStatus + | Ok(x) => Validating(x)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok("") as result => Dirty(result, Shown)->setStatus + | Ok(x) => Validating(x)->setStatus + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input), Hidden)->setStatus + }; + }; + + let validateFieldOfCollectionOfStringTypeOnChangeInOnChangeMode = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus(string, 'message), + ~submissionStatus: submissionStatus, + ~validator: + valueOfCollectionValidator('input, string, 'message, 'action), + ~setStatus: fieldStatus(string, 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok("") as result + | Error(_) as result => Dirty(result, Shown)->setStatus + | Ok(x) => Validating(x)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok("") as result => Dirty(result, Shown)->setStatus + | Ok(x) => Validating(x)->setStatus + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus + }; + }; + + let validateFieldOfOptionStringTypeOnChangeInOnChangeMode = + ( + ~input: 'input, + ~fieldStatus: fieldStatus(option(string), 'message), + ~submissionStatus: submissionStatus, + ~validator: + singleValueValidator('input, option(string), 'message, 'action), + ~setStatus: fieldStatus(option(string), 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok(Some("")) as result + | Ok(None) as result + | Error(_) as result => Dirty(result, Shown)->setStatus + | Ok(Some(_) as x) => Validating(x)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input)) { + | Ok(Some("")) as result + | Ok(None) as result => Dirty(result, Shown)->setStatus + | Ok(Some(_) as x) => Validating(x)->setStatus + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input), Hidden)->setStatus + }; + }; + + let validateFieldOfCollectionOfOptionStringTypeOnChangeInOnChangeMode = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus(option(string), 'message), + ~submissionStatus: submissionStatus, + ~validator: + valueOfCollectionValidator( + 'input, + option(string), + 'message, + 'action, + ), + ~setStatus: fieldStatus(option(string), 'message) => 'statuses, + ) + : 'statuses => { + switch (validator.strategy, fieldStatus, submissionStatus) { + | (_, Dirty(_, Shown), _) + | (_, _, AttemptedToSubmit) + | (OnFirstChange, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok(Some("")) as result + | Ok(None) as result + | Error(_) as result => Dirty(result, Shown)->setStatus + | Ok(Some(_) as x) => Validating(x)->setStatus + } + | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, NeverSubmitted) => + switch (validator.validate(input, ~at=index)) { + | Ok(Some("")) as result + | Ok(None) as result => Dirty(result, Shown)->setStatus + | Ok(Some(_) as x) => Validating(x)->setStatus + | Error(_) as result => Dirty(result, Hidden)->setStatus + } + | (OnFirstBlur | OnSubmit, _, NeverSubmitted) => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus + }; + }; + + let validateDependentFieldOnChange = + ( + ~input: 'input, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~validator: + singleValueValidator('input, 'outputValue, 'message, 'action), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Pristine + | Validating(_) + | Dirty(_, Hidden) => None + | Dirty(_, Shown) => + Dirty(validator.validate(input), Shown)->setStatus->Some + }; + }; + + let validateDependentFieldOfCollectionOnChange = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~validator: + valueOfCollectionValidator( + 'input, + 'outputValue, + 'message, + 'action, + ), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Pristine + | Validating(_) + | Dirty(_, Hidden) => None + | Dirty(_, Shown) => + Dirty(validator.validate(input, ~at=index), Shown)->setStatus->Some + }; + }; + + let validateFieldOnBlur = + ( + ~input: 'input, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~validator: + singleValueValidator('input, 'outputValue, 'message, 'action), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Validating(_) + | Dirty(_, Shown) => None + | Pristine + | Dirty(_, Hidden) => + switch (validator.strategy) { + | OnFirstChange + | OnFirstSuccess + | OnSubmit => Dirty(validator.validate(input), Hidden)->setStatus->Some + | OnFirstBlur + | OnFirstSuccessOrFirstBlur => + switch (validator.validate(input)) { + | Ok(x) => Validating(x)->setStatus->Some + | Error(_) as result => Dirty(result, Shown)->setStatus->Some + } + } + }; + }; + + let validateFieldOfCollectionOnBlur = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~validator: + valueOfCollectionValidator( + 'input, + 'outputValue, + 'message, + 'action, + ), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Validating(_) + | Dirty(_, Shown) => None + | Pristine + | Dirty(_, Hidden) => + switch (validator.strategy) { + | OnFirstChange + | OnFirstSuccess + | OnSubmit => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus->Some + | OnFirstBlur + | OnFirstSuccessOrFirstBlur => + switch (validator.validate(input, ~at=index)) { + | Ok(x) => Validating(x)->setStatus->Some + | Error(_) as result => Dirty(result, Shown)->setStatus->Some + } + } + }; + }; + + let validateFieldOfOptionTypeOnBlur = + ( + ~input: 'input, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~validator: + singleValueValidator('input, 'outputValue, 'message, 'action), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Validating(_) + | Dirty(_, Shown) => None + | Pristine + | Dirty(_, Hidden) => + switch (validator.strategy) { + | OnFirstChange + | OnFirstSuccess + | OnSubmit => Dirty(validator.validate(input), Hidden)->setStatus->Some + | OnFirstBlur + | OnFirstSuccessOrFirstBlur => + switch (validator.validate(input)) { + | Ok(Some(_) as x) => Validating(x)->setStatus->Some + | Ok(None) as result + | Error(_) as result => Dirty(result, Shown)->setStatus->Some + } + } + }; + }; + + let validateFieldOfCollectionOfOptionTypeOnBlur = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus('outputValue, 'message), + ~validator: + valueOfCollectionValidator( + 'input, + 'outputValue, + 'message, + 'action, + ), + ~setStatus: fieldStatus('outputValue, 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Validating(_) + | Dirty(_, Shown) => None + | Pristine + | Dirty(_, Hidden) => + switch (validator.strategy) { + | OnFirstChange + | OnFirstSuccess + | OnSubmit => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus->Some + | OnFirstBlur + | OnFirstSuccessOrFirstBlur => + switch (validator.validate(input, ~at=index)) { + | Ok(Some(_) as x) => Validating(x)->setStatus->Some + | Ok(None) as result + | Error(_) as result => Dirty(result, Shown)->setStatus->Some + } + } + }; + }; + + let validateFieldOfStringTypeOnBlur = + ( + ~input: 'input, + ~fieldStatus: fieldStatus(string, 'message), + ~validator: singleValueValidator('input, string, 'message, 'action), + ~setStatus: fieldStatus(string, 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Validating(_) + | Dirty(_, Shown) => None + | Pristine + | Dirty(_, Hidden) => + switch (validator.strategy) { + | OnFirstChange + | OnFirstSuccess + | OnSubmit => Dirty(validator.validate(input), Hidden)->setStatus->Some + | OnFirstBlur + | OnFirstSuccessOrFirstBlur => + switch (validator.validate(input)) { + | Ok("") as result + | Error(_) as result => Dirty(result, Shown)->setStatus->Some + | Ok(x) => Validating(x)->setStatus->Some + } + } + }; + }; + + let validateFieldOfCollectionOfStringTypeOnBlur = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus(string, 'message), + ~validator: + valueOfCollectionValidator('input, string, 'message, 'action), + ~setStatus: fieldStatus(string, 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Validating(_) + | Dirty(_, Shown) => None + | Pristine + | Dirty(_, Hidden) => + switch (validator.strategy) { + | OnFirstChange + | OnFirstSuccess + | OnSubmit => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus->Some + | OnFirstBlur + | OnFirstSuccessOrFirstBlur => + switch (validator.validate(input, ~at=index)) { + | Ok("") as result + | Error(_) as result => Dirty(result, Shown)->setStatus->Some + | Ok(x) => Validating(x)->setStatus->Some + } + } + }; + }; + + let validateFieldOfOptionStringTypeOnBlur = + ( + ~input: 'input, + ~fieldStatus: fieldStatus(option(string), 'message), + ~validator: + singleValueValidator('input, option(string), 'message, 'action), + ~setStatus: fieldStatus(option(string), 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Validating(_) + | Dirty(_, Shown) => None + | Pristine + | Dirty(_, Hidden) => + switch (validator.strategy) { + | OnFirstChange + | OnFirstSuccess + | OnSubmit => Dirty(validator.validate(input), Hidden)->setStatus->Some + | OnFirstBlur + | OnFirstSuccessOrFirstBlur => + switch (validator.validate(input)) { + | Ok(Some("")) as result + | Ok(None) as result + | Error(_) as result => Dirty(result, Shown)->setStatus->Some + | Ok(Some(_) as x) => Validating(x)->setStatus->Some + } + } + }; + }; + + let validateFieldOfCollectionOfOptionStringTypeOnBlur = + ( + ~input: 'input, + ~index: index, + ~fieldStatus: fieldStatus(option(string), 'message), + ~validator: + valueOfCollectionValidator( + 'input, + option(string), + 'message, + 'action, + ), + ~setStatus: fieldStatus(option(string), 'message) => 'statuses, + ) + : option('statuses) => { + switch (fieldStatus) { + | Validating(_) + | Dirty(_, Shown) => None + | Pristine + | Dirty(_, Hidden) => + switch (validator.strategy) { + | OnFirstChange + | OnFirstSuccess + | OnSubmit => + Dirty(validator.validate(input, ~at=index), Hidden)->setStatus->Some + | OnFirstBlur + | OnFirstSuccessOrFirstBlur => + switch (validator.validate(input, ~at=index)) { + | Ok(Some("")) as result + | Ok(None) as result + | Error(_) as result => Dirty(result, Shown)->setStatus->Some + | Ok(Some(_) as x) => Validating(x)->setStatus->Some + } + } + }; + }; +}; diff --git a/lib/src/FormalityCompat.re b/lib/src/FormalityCompat.re new file mode 100644 index 00000000..c62d470f --- /dev/null +++ b/lib/src/FormalityCompat.re @@ -0,0 +1,21 @@ +include FormalityCompat__Validation.Result; +include FormalityCompat__Validation.Sync; +include FormalityCompat__PublicHelpers; + +module Strategy = FormalityCompat__Strategy; +module FormStatus = FormalityCompat__FormStatus; + +module Make = FormalityCompat__Form.Make; +module MakeWithId = FormalityCompat__FormWithId.Make; + +module Async = { + include FormalityCompat__Validation.Async; + + module Make = FormalityCompat__FormAsyncOnChange.Make; + module MakeWithId = FormalityCompat__FormAsyncOnChangeWithId.Make; + + module MakeOnBlur = FormalityCompat__FormAsyncOnBlur.Make; + module MakeOnBlurWithId = FormalityCompat__FormAsyncOnBlurWithId.Make; + + let debounceInterval = FormalityCompat__FormAsyncOnChangeWithId.defaultDebounceInterval; +}; diff --git a/lib/src/FormalityCompat.rei b/lib/src/FormalityCompat.rei new file mode 100644 index 00000000..2a6c1045 --- /dev/null +++ b/lib/src/FormalityCompat.rei @@ -0,0 +1,64 @@ +module Dom = FormalityCompat__PublicHelpers.Dom; +module Strategy = FormalityCompat__Strategy; +module FormStatus = FormalityCompat__FormStatus; + +module Make = FormalityCompat__Form.Make; +module MakeWithId = FormalityCompat__FormWithId.Make; + +type ok = FormalityCompat__Validation.Result.ok = | Valid | NoValue; + +type result('message) = Belt.Result.t(ok, 'message); + +type status('message) = + FormalityCompat__Validation.Sync.status('message) = + | Pristine + | Dirty( + FormalityCompat__Validation.Result.result('message), + FormalityCompat__Validation.Visibility.t, + ); + +type validate('state, 'message) = + 'state => FormalityCompat__Validation.Result.result('message); + +type validator('field, 'state, 'message) = + FormalityCompat__Validation.Sync.validator('field, 'state, 'message) = { + field: 'field, + strategy: FormalityCompat__Strategy.t, + dependents: option(list('field)), + validate: validate('state, 'message), + }; + +module Async: { + module Make = FormalityCompat__FormAsyncOnChange.Make; + module MakeWithId = FormalityCompat__FormAsyncOnChangeWithId.Make; + + module MakeOnBlur = FormalityCompat__FormAsyncOnBlur.Make; + module MakeOnBlurWithId = FormalityCompat__FormAsyncOnBlurWithId.Make; + + let debounceInterval: int; + + type status('message) = + FormalityCompat__Validation.Async.status('message) = + | Pristine + | Dirty( + FormalityCompat__Validation.Result.result('message), + FormalityCompat__Validation.Visibility.t, + ) + | Validating; + + type validate('state, 'message) = + 'state => + Js.Promise.t(FormalityCompat__Validation.Result.result('message)); + + type equalityChecker('state) = ('state, 'state) => bool; + + type validator('field, 'state, 'message) = + FormalityCompat__Validation.Async.validator('field, 'state, 'message) = { + field: 'field, + strategy: FormalityCompat__Strategy.t, + dependents: option(list('field)), + validate: FormalityCompat__Validation.Sync.validate('state, 'message), + validateAsync: + option((validate('state, 'message), equalityChecker('state))), + }; +}; diff --git a/src/Formality__Form.re b/lib/src/FormalityCompat__Form.re similarity index 77% rename from src/Formality__Form.re rename to lib/src/FormalityCompat__Form.re index 0bb28ba0..f5c8ac26 100644 --- a/src/Formality__Form.re +++ b/lib/src/FormalityCompat__Form.re @@ -1,4 +1,4 @@ -module Validation = Formality__FormWithId.Validation; +module Validation = FormalityCompat__FormWithId.Validation; module type Form = { type field; @@ -9,7 +9,7 @@ module type Form = { }; module Make = (Form: Form) => - Formality__FormWithId.Make({ + FormalityCompat__FormWithId.Make({ include Form; module FieldId = Id.MakeComparable({ diff --git a/src/Formality__FormAsyncOnBlur.re b/lib/src/FormalityCompat__FormAsyncOnBlur.re similarity index 74% rename from src/Formality__FormAsyncOnBlur.re rename to lib/src/FormalityCompat__FormAsyncOnBlur.re index 7fc33cc1..19b0fb5d 100644 --- a/src/Formality__FormAsyncOnBlur.re +++ b/lib/src/FormalityCompat__FormAsyncOnBlur.re @@ -1,4 +1,4 @@ -module Validation = Formality__FormAsyncOnBlurWithId.Validation; +module Validation = FormalityCompat__FormAsyncOnBlurWithId.Validation; module type Form = { type field; @@ -9,7 +9,7 @@ module type Form = { }; module Make = (Form: Form) => - Formality__FormAsyncOnBlurWithId.Make({ + FormalityCompat__FormAsyncOnBlurWithId.Make({ include Form; module FieldId = Id.MakeComparable({ diff --git a/src/Formality__FormAsyncOnBlurWithId.re b/lib/src/FormalityCompat__FormAsyncOnBlurWithId.re similarity index 98% rename from src/Formality__FormAsyncOnBlurWithId.re rename to lib/src/FormalityCompat__FormAsyncOnBlurWithId.re index 36ce214a..60de4602 100644 --- a/src/Formality__FormAsyncOnBlurWithId.re +++ b/lib/src/FormalityCompat__FormAsyncOnBlurWithId.re @@ -1,7 +1,7 @@ -module Validation = Formality__Validation; -module Strategy = Formality__Strategy; -module FormStatus = Formality__FormStatus; -module ReactUpdate = Formality__ReactUpdate; +module Validation = FormalityCompat__Validation; +module Strategy = FormalityCompat__Strategy; +module FormStatus = FormalityCompat__FormStatus; +module ReactUpdate = FormalityCompat__ReactUpdate; module type Form = { type field; @@ -236,7 +236,6 @@ module Make = (Form: Form) => { }) | OnFirstBlur | OnFirstSuccessOrFirstBlur => - let result = state.input->(validator.validate); switch (result, validator.validateAsync) { | (_, None) => Update({ @@ -269,7 +268,7 @@ module Make = (Form: Form) => { ...state, fields: state.fields->Map.set(field, Dirty(result, Shown)), }) - }; + } }; }; diff --git a/src/Formality__FormAsyncOnChange.re b/lib/src/FormalityCompat__FormAsyncOnChange.re similarity index 63% rename from src/Formality__FormAsyncOnChange.re rename to lib/src/FormalityCompat__FormAsyncOnChange.re index 1c4d707f..0ae8a183 100644 --- a/src/Formality__FormAsyncOnChange.re +++ b/lib/src/FormalityCompat__FormAsyncOnChange.re @@ -1,6 +1,6 @@ -module Validation = Formality__FormAsyncOnChangeWithId.Validation; +module Validation = FormalityCompat__FormAsyncOnChangeWithId.Validation; -let defaultDebounceInterval = Formality__FormAsyncOnChangeWithId.defaultDebounceInterval; +let defaultDebounceInterval = FormalityCompat__FormAsyncOnChangeWithId.defaultDebounceInterval; module type Form = { type field; @@ -12,7 +12,7 @@ module type Form = { }; module Make = (Form: Form) => - Formality__FormAsyncOnChangeWithId.Make({ + FormalityCompat__FormAsyncOnChangeWithId.Make({ include Form; module FieldId = Id.MakeComparable({ diff --git a/src/Formality__FormAsyncOnChangeWithId.re b/lib/src/FormalityCompat__FormAsyncOnChangeWithId.re similarity index 98% rename from src/Formality__FormAsyncOnChangeWithId.re rename to lib/src/FormalityCompat__FormAsyncOnChangeWithId.re index b911f3fc..49206694 100644 --- a/src/Formality__FormAsyncOnChangeWithId.re +++ b/lib/src/FormalityCompat__FormAsyncOnChangeWithId.re @@ -1,7 +1,7 @@ -module Validation = Formality__Validation; -module Strategy = Formality__Strategy; -module FormStatus = Formality__FormStatus; -module ReactUpdate = Formality__ReactUpdate; +module Validation = FormalityCompat__Validation; +module Strategy = FormalityCompat__Strategy; +module FormStatus = FormalityCompat__FormStatus; +module ReactUpdate = FormalityCompat__ReactUpdate; let defaultDebounceInterval = 700; diff --git a/src/Formality__FormStatus.re b/lib/src/FormalityCompat__FormStatus.re similarity index 100% rename from src/Formality__FormStatus.re rename to lib/src/FormalityCompat__FormStatus.re diff --git a/src/Formality__FormWithId.re b/lib/src/FormalityCompat__FormWithId.re similarity index 98% rename from src/Formality__FormWithId.re rename to lib/src/FormalityCompat__FormWithId.re index fb5851ce..7c35caaf 100644 --- a/src/Formality__FormWithId.re +++ b/lib/src/FormalityCompat__FormWithId.re @@ -1,7 +1,7 @@ -module Validation = Formality__Validation; -module Strategy = Formality__Strategy; -module FormStatus = Formality__FormStatus; -module ReactUpdate = Formality__ReactUpdate; +module Validation = FormalityCompat__Validation; +module Strategy = FormalityCompat__Strategy; +module FormStatus = FormalityCompat__FormStatus; +module ReactUpdate = FormalityCompat__ReactUpdate; module type Form = { type field; diff --git a/src/Formality__PublicHelpers.re b/lib/src/FormalityCompat__PublicHelpers.re similarity index 100% rename from src/Formality__PublicHelpers.re rename to lib/src/FormalityCompat__PublicHelpers.re diff --git a/src/Formality__PublicHelpers.rei b/lib/src/FormalityCompat__PublicHelpers.rei similarity index 100% rename from src/Formality__PublicHelpers.rei rename to lib/src/FormalityCompat__PublicHelpers.rei diff --git a/lib/src/FormalityCompat__ReactUpdate.re b/lib/src/FormalityCompat__ReactUpdate.re new file mode 100644 index 00000000..e2db5c40 --- /dev/null +++ b/lib/src/FormalityCompat__ReactUpdate.re @@ -0,0 +1,42 @@ +[@ocaml.warning "-30"]; + +type update('action, 'state) = + | NoUpdate + | Update('state) + | UpdateWithSideEffects('state, self('state, 'action) => unit) +and dispatch('action) = 'action => unit +and self('state, 'action) = { + state: 'state, + dispatch: dispatch('action), +} +and fullState('state, 'action) = { + state: 'state, + sideEffects: ref(array(self('state, 'action) => unit)), +}; + +let useReducer = (initialState, reducer) => { + let ({state, sideEffects}, dispatch) = + React.useReducer( + ({state, sideEffects} as fullState, action) => + switch (reducer(state, action)) { + | NoUpdate => fullState + | Update(state) => {...fullState, state} + | UpdateWithSideEffects(state, sideEffect) => { + state, + sideEffects: Array.concat(sideEffects^, [|sideEffect|])->ref, + } + }, + {state: initialState, sideEffects: [||]->ref}, + ); + React.useEffect1( + () => { + if (Array.length(sideEffects^) > 0) { + Array.forEach(sideEffects^, fn => fn({state, dispatch})); + sideEffects := [||]; + }; + None; + }, + [|sideEffects^|], + ); + (state, dispatch); +}; diff --git a/src/Formality__Strategy.re b/lib/src/FormalityCompat__Strategy.re similarity index 100% rename from src/Formality__Strategy.re rename to lib/src/FormalityCompat__Strategy.re diff --git a/src/Formality__Validation.re b/lib/src/FormalityCompat__Validation.re similarity index 92% rename from src/Formality__Validation.re rename to lib/src/FormalityCompat__Validation.re index b7bbed27..f24c28ef 100644 --- a/src/Formality__Validation.re +++ b/lib/src/FormalityCompat__Validation.re @@ -1,3 +1,5 @@ +module Startegy = FormalityCompat__Strategy; + module Result = { type ok = | Valid @@ -21,7 +23,7 @@ module Sync = { type validator('field, 'state, 'message) = { field: 'field, - strategy: Formality__Strategy.t, + strategy: Startegy.t, dependents: option(list('field)), validate: validate('state, 'message), }; @@ -42,7 +44,7 @@ module Async = { type validator('field, 'state, 'message) = { field: 'field, - strategy: Formality__Strategy.t, + strategy: Startegy.t, dependents: option(list('field)), validate: Sync.validate('state, 'message), validateAsync: diff --git a/lib/src/Formality__Debouncer.re b/lib/src/Formality__Debouncer.re new file mode 100644 index 00000000..ded23dcc --- /dev/null +++ b/lib/src/Formality__Debouncer.re @@ -0,0 +1 @@ +include Debouncer; diff --git a/src/Formality__ReactUpdate.re b/lib/src/Formality__ReactUpdate.re similarity index 83% rename from src/Formality__ReactUpdate.re rename to lib/src/Formality__ReactUpdate.re index 1b013204..0d140cc4 100644 --- a/src/Formality__ReactUpdate.re +++ b/lib/src/Formality__ReactUpdate.re @@ -1,4 +1,6 @@ -type update('action, 'state) = +[@ocaml.warning "-30"]; + +type update('state, 'action) = | NoUpdate | Update('state) | UpdateWithSideEffects('state, self('state, 'action) => unit) @@ -12,7 +14,9 @@ and fullState('state, 'action) = { sideEffects: ref(array(self('state, 'action) => unit)), }; -let useReducer = (initialState, reducer) => { +type reducer('state, 'action) = ('state, 'action) => update('state, 'action); + +let useReducer = (initialState: 'state, reducer: reducer('state, 'action)) => { let ({state, sideEffects}, dispatch) = React.useReducer( ({state, sideEffects} as fullState, action) => diff --git a/lib/test/Case.re b/lib/test/Case.re new file mode 100644 index 00000000..4d1ff90f --- /dev/null +++ b/lib/test/Case.re @@ -0,0 +1,132 @@ +type result = { + actual: (string, string), + expected: (string, string), +}; + +let testable = Alcotest.(pair(string, string)); +let nothing = ""; + +module Path = { + let join = xs => xs |> String.concat(Filename.dir_sep); + + let test_cases_dir = Filename.([current_dir_name, "test", "cases"] |> join); + + let source = x => Filename.concat(test_cases_dir, x ++ ".re"); + let snapshot = x => Filename.concat(test_cases_dir, x ++ ".snapshot"); + + let ppx = + Filename.( + concat( + [current_dir_name, "_build", "default", "bin"] |> join, + "bin.exe", + ) + ); + let bsc = + Filename.( + concat([current_dir_name, "node_modules", ".bin"] |> join, "bsc") + ); + let refmt = + Filename.( + concat([current_dir_name, "node_modules", ".bin"] |> join, "bsrefmt") + ); + let reason_react = + Filename.( + [parent_dir_name, "node_modules", "reason-react", "lib", "ocaml"] + |> join + ); + let re_formality = + Filename.( + [parent_dir_name, "node_modules", "re-formality", "lib", "ocaml"] + |> join + ); +}; + +module Bsc = { + let errors = "+A"; + + let cmd = case => + Path.( + bsc + ++ " -ppx " + ++ ppx + ++ " -I " + ++ re_formality + ++ " -I " + ++ reason_react + ++ " -w " + ++ errors + ++ " -warn-error " + ++ errors + ++ " -color never" + ++ " -bs-cmi-only " + ++ (case |> source) + ); +}; + +let env = { + let path = () => "PATH=" ++ Sys.getenv("PATH"); + let systemroot = () => "SYSTEMROOT=" ++ Sys.getenv("SYSTEMROOT"); + switch (Sys.os_type) { + | "Win32" => [|path(), systemroot()|] + | _ => [|path()|] + }; +}; + +let read_from_channel = channel => { + let buffer = Buffer.create(1024); + let newline = "\n"; + try( + while (true) { + channel |> input_line |> Buffer.add_string(buffer); + newline |> Buffer.add_string(buffer); + } + ) { + | End_of_file => () + }; + + buffer |> Buffer.contents; +}; + +let run_bsc = case => { + let (stdout, stdin, stderr) = Unix.open_process_full(case |> Bsc.cmd, env); + + let res = (stdout |> read_from_channel, stderr |> read_from_channel); + + Unix.close_process_full((stdout, stdin, stderr)) |> ignore; + + res; +}; + +let diff_error_snapshot = case => { + let actual = case |> Bsc.cmd; + let snapshot = case |> Path.snapshot; + + let cmd = + actual + ++ " 2>&1" + ++ ( + // FIXME: It doesn't work on CI + switch (Sys.os_type) { + | "Win32" => {| | sed "s|\\test\\cases\\|/test/cases/|g"|} + | _ => "" + } + ) + ++ {| | diff --ignore-blank-lines --ignore-space-change |} + ++ snapshot + ++ {| -|}; + + let (stdout, stdin, stderr) = Unix.open_process_full(cmd, env); + + let res = (stdout |> read_from_channel, stderr |> read_from_channel); + + Unix.close_process_full((stdout, stdin, stderr)) |> ignore; + + res; +}; + +let ok = case => {actual: case |> run_bsc, expected: (nothing, nothing)}; + +let error = case => { + actual: case |> diff_error_snapshot, + expected: (nothing, nothing), +}; diff --git a/lib/test/Case.rei b/lib/test/Case.rei new file mode 100644 index 00000000..a9aab96a --- /dev/null +++ b/lib/test/Case.rei @@ -0,0 +1,9 @@ +type result = { + actual: (string, string), + expected: (string, string), +}; + +let testable: Alcotest.testable((string, string)); + +let ok: string => result; +let error: string => result; diff --git a/lib/test/README.md b/lib/test/README.md new file mode 100644 index 00000000..c105eb4a --- /dev/null +++ b/lib/test/README.md @@ -0,0 +1,40 @@ +# PPX tests +These tests ensure that there are no errors/warnings during a compilation of valid forms and check proper error messages produced by PPX itself. + +Before running tests: + +```shell +# Install yarn deps +yarn install + +# Install esy deps +cd lib +esy install + +# Build public interface of the lib +$(yarn bin)/bsb -install +``` + +To run tests: + +```shell +esy x test.exe +``` + +To inspect result produced by specific case: + +```shell +test/script/print-bsc-output [CASE_MODULE_NAME_WITHOUT_EXTENSION] +``` + +To write expected output for specific error case: + +```shell +test/script/write-error-snapshot [CASE_MODULE_NAME_WITHOUT_EXTENSION] +``` + +To write un-ppx'ed source of a test case to sandbox for debugging: + +```shell +test/script/sandbox [CASE_MODULE_NAME_WITHOUT_EXTENSION] +``` diff --git a/lib/test/Test.re b/lib/test/Test.re new file mode 100644 index 00000000..572a4e02 --- /dev/null +++ b/lib/test/Test.re @@ -0,0 +1,54 @@ +let check = (Case.{expected, actual}) => + Alcotest.(check(Case.testable, "same string", expected, actual)); + +let ok = case => { + Alcotest.test_case(case, `Quick, () => case |> Case.ok |> check); +}; + +let error = case => { + Alcotest.test_case(case, `Quick, () => case |> Case.error |> check); +}; + +let () = + Alcotest.( + run( + "Ppx", + [ + ( + "oks", + [ + "Ok__FieldWithNoValidator", + "Ok__FieldWithSyncValidator", + "Ok__FieldWithAsyncValidatorInOnChangeMode", + "Ok__FieldWithAsyncValidatorInOnBlurMode", + "Ok__FieldWithSyncValidatorAndFieldWithAsyncValidatorInOnChangeMode", + "Ok__FieldWithSyncValidatorAndFieldWithAsyncValidatorInOnBlurMode", + "Ok__FieldWithSyncValidatorAndDependentFieldAndFieldWithSyncValidator", + "Ok__TwoFieldsWithNoValidators", + "Ok__TwoFieldsWithSyncValidators", + "Ok__TwoFieldsWithAsyncValidatorsInOnChangeMode", + "Ok__TwoFieldsWithAsyncValidatorsInOnBlurMode", + "Ok__TwoFieldsWithSyncValidatorAndFieldWithAsyncValidatorInOnChangeMode", + "Ok__TwoFieldsWithSyncValidatorAndFieldWithAsyncValidatorInOnBlurMode", + "Ok__FieldWithSyncValidatorAndFieldWithNoValidator", + "Ok__FieldWithSyncValidatorAndCollectionWithNoCollectionValidatorAndFieldWithSyncValidator", + "Ok__CollectionWithNoCollectionValidatorAndFieldOfCollectionWithSyncValidator", + "Ok__CollectionWithCollectionValidatorAndFieldOfCollectionWithSyncValidator", + "Ok__CollectionWithNoCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnChangeMode", + "Ok__CollectionWithNoCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnBlurMode", + "Ok__CollectionWithCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnChangeMode", + "Ok__CollectionWithCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnBlurMode", + "Ok__CollectionWithNoCollectionValidatorAndTwoFieldsOfCollectionWithSyncValidator", + "Ok__Message", + "Ok__SubmissionError", + ] + |> List.map(ok), + ), + ( + "errors", + ["Error__InputNotFound", "Error__InputNotRecord"] + |> List.map(error), + ), + ], + ) + ); diff --git a/lib/test/cases/Error__InputNotFound.re b/lib/test/cases/Error__InputNotFound.re new file mode 100644 index 00000000..e2ba3e5f --- /dev/null +++ b/lib/test/cases/Error__InputNotFound.re @@ -0,0 +1 @@ +module Form = [%form type t]; diff --git a/lib/test/cases/Error__InputNotFound.snapshot b/lib/test/cases/Error__InputNotFound.snapshot new file mode 100644 index 00000000..748eb093 --- /dev/null +++ b/lib/test/cases/Error__InputNotFound.snapshot @@ -0,0 +1,8 @@ + + We've found a bug for you! + ./test/cases/Error__InputNotFound.re 1:15-28 + + 1 │ module Form = [%form type t]; + + `input` type not found + diff --git a/lib/test/cases/Error__InputNotRecord.re b/lib/test/cases/Error__InputNotRecord.re new file mode 100644 index 00000000..7d5a8a14 --- /dev/null +++ b/lib/test/cases/Error__InputNotRecord.re @@ -0,0 +1 @@ +module Form = [%form type input = int]; diff --git a/lib/test/cases/Error__InputNotRecord.snapshot b/lib/test/cases/Error__InputNotRecord.snapshot new file mode 100644 index 00000000..cdfed0ee --- /dev/null +++ b/lib/test/cases/Error__InputNotRecord.snapshot @@ -0,0 +1,8 @@ + + We've found a bug for you! + ./test/cases/Error__InputNotRecord.re 1:22-37 + + 1 │ module Form = [%form type input = int]; + + `input` must be of record type + diff --git a/lib/test/cases/Ok__CollectionWithCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnBlurMode.re b/lib/test/cases/Ok__CollectionWithCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnBlurMode.re new file mode 100644 index 00000000..d6be8ae1 --- /dev/null +++ b/lib/test/cases/Ok__CollectionWithCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnBlurMode.re @@ -0,0 +1,17 @@ +module Form = [%form + type input = {authors: [@field.collection] array(author)} + and author = {name: [@field.async {mode: OnBlur}] string}; + let validators = { + authors: { + collection: _input => Ok(), + fields: { + name: { + strategy: OnSubmit, + validate: ({authors, _}, ~at) => + Ok(authors->Belt.Array.getUnsafe(at).name), + validateAsync: name => Js.Promise.resolve(Ok(name)), + }, + }, + }, + } +]; diff --git a/lib/test/cases/Ok__CollectionWithCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnChangeMode.re b/lib/test/cases/Ok__CollectionWithCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnChangeMode.re new file mode 100644 index 00000000..1e979bf8 --- /dev/null +++ b/lib/test/cases/Ok__CollectionWithCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnChangeMode.re @@ -0,0 +1,17 @@ +module Form = [%form + type input = {authors: [@field.collection] array(author)} + and author = {name: [@field.async] string}; + let validators = { + authors: { + collection: _input => Ok(), + fields: { + name: { + strategy: OnSubmit, + validate: ({authors, _}, ~at) => + Ok(authors->Belt.Array.getUnsafe(at).name), + validateAsync: name => Js.Promise.resolve(Ok(name)), + }, + }, + }, + } +]; diff --git a/lib/test/cases/Ok__CollectionWithCollectionValidatorAndFieldOfCollectionWithSyncValidator.re b/lib/test/cases/Ok__CollectionWithCollectionValidatorAndFieldOfCollectionWithSyncValidator.re new file mode 100644 index 00000000..12794183 --- /dev/null +++ b/lib/test/cases/Ok__CollectionWithCollectionValidatorAndFieldOfCollectionWithSyncValidator.re @@ -0,0 +1,16 @@ +module Form = [%form + type input = {authors: [@field.collection] array(author)} + and author = {name: string}; + let validators = { + authors: { + collection: _input => Ok(), + fields: { + name: { + strategy: OnSubmit, + validate: ({authors, _}, ~at) => + Ok(authors->Belt.Array.getUnsafe(at).name), + }, + }, + }, + } +]; diff --git a/lib/test/cases/Ok__CollectionWithNoCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnBlurMode.re b/lib/test/cases/Ok__CollectionWithNoCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnBlurMode.re new file mode 100644 index 00000000..b4bcde72 --- /dev/null +++ b/lib/test/cases/Ok__CollectionWithNoCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnBlurMode.re @@ -0,0 +1,17 @@ +module Form = [%form + type input = {authors: [@field.collection] array(author)} + and author = {name: [@field.async {mode: OnBlur}] string}; + let validators = { + authors: { + collection: None, + fields: { + name: { + strategy: OnSubmit, + validate: ({authors, _}, ~at) => + Ok(authors->Belt.Array.getUnsafe(at).name), + validateAsync: name => Js.Promise.resolve(Ok(name)), + }, + }, + }, + } +]; diff --git a/lib/test/cases/Ok__CollectionWithNoCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnChangeMode.re b/lib/test/cases/Ok__CollectionWithNoCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnChangeMode.re new file mode 100644 index 00000000..7d01662a --- /dev/null +++ b/lib/test/cases/Ok__CollectionWithNoCollectionValidatorAndFieldOfCollectionWithAsyncValidatorInOnChangeMode.re @@ -0,0 +1,17 @@ +module Form = [%form + type input = {authors: [@field.collection] array(author)} + and author = {name: [@field.async] string}; + let validators = { + authors: { + collection: None, + fields: { + name: { + strategy: OnSubmit, + validate: ({authors, _}, ~at) => + Ok(authors->Belt.Array.getUnsafe(at).name), + validateAsync: name => Js.Promise.resolve(Ok(name)), + }, + }, + }, + } +]; diff --git a/lib/test/cases/Ok__CollectionWithNoCollectionValidatorAndFieldOfCollectionWithSyncValidator.re b/lib/test/cases/Ok__CollectionWithNoCollectionValidatorAndFieldOfCollectionWithSyncValidator.re new file mode 100644 index 00000000..42f4c17c --- /dev/null +++ b/lib/test/cases/Ok__CollectionWithNoCollectionValidatorAndFieldOfCollectionWithSyncValidator.re @@ -0,0 +1,16 @@ +module Form = [%form + type input = {authors: [@field.collection] array(author)} + and author = {name: string}; + let validators = { + authors: { + collection: None, + fields: { + name: { + strategy: OnSubmit, + validate: ({authors, _}, ~at) => + Ok(authors->Belt.Array.getUnsafe(at).name), + }, + }, + }, + } +]; diff --git a/lib/test/cases/Ok__CollectionWithNoCollectionValidatorAndTwoFieldsOfCollectionWithSyncValidator.re b/lib/test/cases/Ok__CollectionWithNoCollectionValidatorAndTwoFieldsOfCollectionWithSyncValidator.re new file mode 100644 index 00000000..efec07ba --- /dev/null +++ b/lib/test/cases/Ok__CollectionWithNoCollectionValidatorAndTwoFieldsOfCollectionWithSyncValidator.re @@ -0,0 +1,24 @@ +module Form = [%form + type input = {authors: [@field.collection] array(author)} + and author = { + name: string, + age: int, + }; + let validators = { + authors: { + collection: None, + fields: { + name: { + strategy: OnSubmit, + validate: ({authors, _}, ~at) => + Ok(authors->Belt.Array.getUnsafe(at).name), + }, + age: { + strategy: OnSubmit, + validate: ({authors, _}, ~at) => + Ok(authors->Belt.Array.getUnsafe(at).age), + }, + }, + }, + } +]; diff --git a/lib/test/cases/Ok__FieldWithAsyncValidatorInOnBlurMode.re b/lib/test/cases/Ok__FieldWithAsyncValidatorInOnBlurMode.re new file mode 100644 index 00000000..380faaf3 --- /dev/null +++ b/lib/test/cases/Ok__FieldWithAsyncValidatorInOnBlurMode.re @@ -0,0 +1,10 @@ +module Form = [%form + type input = {name: [@field.async {mode: OnBlur}] string}; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name}) => Ok(name), + validateAsync: name => Js.Promise.resolve(Ok(name)), + }, + } +]; diff --git a/lib/test/cases/Ok__FieldWithAsyncValidatorInOnChangeMode.re b/lib/test/cases/Ok__FieldWithAsyncValidatorInOnChangeMode.re new file mode 100644 index 00000000..b6b65eb5 --- /dev/null +++ b/lib/test/cases/Ok__FieldWithAsyncValidatorInOnChangeMode.re @@ -0,0 +1,10 @@ +module Form = [%form + type input = {name: [@field.async] string}; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name}) => Ok(name), + validateAsync: name => Js.Promise.resolve(Ok(name)), + }, + } +]; diff --git a/lib/test/cases/Ok__FieldWithNoValidator.re b/lib/test/cases/Ok__FieldWithNoValidator.re new file mode 100644 index 00000000..801a8039 --- /dev/null +++ b/lib/test/cases/Ok__FieldWithNoValidator.re @@ -0,0 +1,4 @@ +module Form = [%form + type input = {name: string}; + let validators = {name: None} +]; diff --git a/lib/test/cases/Ok__FieldWithSyncValidator.re b/lib/test/cases/Ok__FieldWithSyncValidator.re new file mode 100644 index 00000000..4a9be1f6 --- /dev/null +++ b/lib/test/cases/Ok__FieldWithSyncValidator.re @@ -0,0 +1,9 @@ +module Form = [%form + type input = {name: string}; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name}) => Ok(name), + }, + } +]; diff --git a/lib/test/cases/Ok__FieldWithSyncValidatorAndCollectionWithNoCollectionValidatorAndFieldWithSyncValidator.re b/lib/test/cases/Ok__FieldWithSyncValidatorAndCollectionWithNoCollectionValidatorAndFieldWithSyncValidator.re new file mode 100644 index 00000000..0d2abbed --- /dev/null +++ b/lib/test/cases/Ok__FieldWithSyncValidatorAndCollectionWithNoCollectionValidatorAndFieldWithSyncValidator.re @@ -0,0 +1,23 @@ +module Form = [%form + type input = { + title: string, + authors: [@field.collection] array(author), + } + and author = {name: string}; + let validators = { + title: { + strategy: OnSubmit, + validate: ({title, _}) => Ok(title), + }, + authors: { + collection: None, + fields: { + name: { + strategy: OnSubmit, + validate: ({authors, _}, ~at) => + Ok(authors->Belt.Array.getUnsafe(at).name), + }, + }, + }, + } +]; diff --git a/lib/test/cases/Ok__FieldWithSyncValidatorAndDependentFieldAndFieldWithSyncValidator.re b/lib/test/cases/Ok__FieldWithSyncValidatorAndDependentFieldAndFieldWithSyncValidator.re new file mode 100644 index 00000000..c74278d0 --- /dev/null +++ b/lib/test/cases/Ok__FieldWithSyncValidatorAndDependentFieldAndFieldWithSyncValidator.re @@ -0,0 +1,16 @@ +module Form = [%form + type input = { + name: [@field.deps age] string, + age: int, + }; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name, _}) => Ok(name), + }, + age: { + strategy: OnSubmit, + validate: ({age, _}) => Ok(age), + }, + } +]; diff --git a/lib/test/cases/Ok__FieldWithSyncValidatorAndFieldWithAsyncValidatorInOnBlurMode.re b/lib/test/cases/Ok__FieldWithSyncValidatorAndFieldWithAsyncValidatorInOnBlurMode.re new file mode 100644 index 00000000..42385a22 --- /dev/null +++ b/lib/test/cases/Ok__FieldWithSyncValidatorAndFieldWithAsyncValidatorInOnBlurMode.re @@ -0,0 +1,17 @@ +module Form = [%form + type input = { + name: [@field.async {mode: OnBlur}] string, + age: int, + }; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name, _}) => Ok(name), + validateAsync: name => Js.Promise.resolve(Ok(name)), + }, + age: { + strategy: OnSubmit, + validate: ({age, _}) => Ok(age), + }, + } +]; diff --git a/lib/test/cases/Ok__FieldWithSyncValidatorAndFieldWithAsyncValidatorInOnChangeMode.re b/lib/test/cases/Ok__FieldWithSyncValidatorAndFieldWithAsyncValidatorInOnChangeMode.re new file mode 100644 index 00000000..6f674bb0 --- /dev/null +++ b/lib/test/cases/Ok__FieldWithSyncValidatorAndFieldWithAsyncValidatorInOnChangeMode.re @@ -0,0 +1,17 @@ +module Form = [%form + type input = { + name: [@field.async] string, + age: int, + }; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name, _}) => Ok(name), + validateAsync: name => Js.Promise.resolve(Ok(name)), + }, + age: { + strategy: OnSubmit, + validate: ({age, _}) => Ok(age), + }, + } +]; diff --git a/lib/test/cases/Ok__FieldWithSyncValidatorAndFieldWithNoValidator.re b/lib/test/cases/Ok__FieldWithSyncValidatorAndFieldWithNoValidator.re new file mode 100644 index 00000000..63a31cfa --- /dev/null +++ b/lib/test/cases/Ok__FieldWithSyncValidatorAndFieldWithNoValidator.re @@ -0,0 +1,13 @@ +module Form = [%form + type input = { + name: string, + age: int, + }; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name, _}) => Ok(name), + }, + age: None, + } +]; diff --git a/lib/test/cases/Ok__Message.re b/lib/test/cases/Ok__Message.re new file mode 100644 index 00000000..a2b1eeec --- /dev/null +++ b/lib/test/cases/Ok__Message.re @@ -0,0 +1,10 @@ +module Form = [%form + type input = {name: string}; + type message = int; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name}) => Ok(name), + }, + } +]; diff --git a/lib/test/cases/Ok__SubmissionError.re b/lib/test/cases/Ok__SubmissionError.re new file mode 100644 index 00000000..da6dd74a --- /dev/null +++ b/lib/test/cases/Ok__SubmissionError.re @@ -0,0 +1,12 @@ +module Form = [%form + type input = {name: string}; + type submissionError = + | A + | B; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name}) => Ok(name), + }, + } +]; diff --git a/lib/test/cases/Ok__TwoFieldsWithAsyncValidatorsInOnBlurMode.re b/lib/test/cases/Ok__TwoFieldsWithAsyncValidatorsInOnBlurMode.re new file mode 100644 index 00000000..cc8901f8 --- /dev/null +++ b/lib/test/cases/Ok__TwoFieldsWithAsyncValidatorsInOnBlurMode.re @@ -0,0 +1,18 @@ +module Form = [%form + type input = { + name: [@field.async {mode: OnBlur}] string, + age: [@field.async {mode: OnBlur}] int, + }; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name, _}) => Ok(name), + validateAsync: name => Js.Promise.resolve(Ok(name)), + }, + age: { + strategy: OnSubmit, + validate: ({age, _}) => Ok(age), + validateAsync: age => Js.Promise.resolve(Ok(age)), + }, + } +]; diff --git a/lib/test/cases/Ok__TwoFieldsWithAsyncValidatorsInOnChangeMode.re b/lib/test/cases/Ok__TwoFieldsWithAsyncValidatorsInOnChangeMode.re new file mode 100644 index 00000000..fe9d8d8a --- /dev/null +++ b/lib/test/cases/Ok__TwoFieldsWithAsyncValidatorsInOnChangeMode.re @@ -0,0 +1,18 @@ +module Form = [%form + type input = { + name: [@field.async] string, + age: [@field.async] int, + }; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name, _}) => Ok(name), + validateAsync: name => Js.Promise.resolve(Ok(name)), + }, + age: { + strategy: OnSubmit, + validate: ({age, _}) => Ok(age), + validateAsync: age => Js.Promise.resolve(Ok(age)), + }, + } +]; diff --git a/lib/test/cases/Ok__TwoFieldsWithNoValidators.re b/lib/test/cases/Ok__TwoFieldsWithNoValidators.re new file mode 100644 index 00000000..11199b92 --- /dev/null +++ b/lib/test/cases/Ok__TwoFieldsWithNoValidators.re @@ -0,0 +1,7 @@ +module Form = [%form + type input = { + name: string, + age: int, + }; + let validators = {name: None, age: None} +]; diff --git a/lib/test/cases/Ok__TwoFieldsWithSyncValidatorAndFieldWithAsyncValidatorInOnBlurMode.re b/lib/test/cases/Ok__TwoFieldsWithSyncValidatorAndFieldWithAsyncValidatorInOnBlurMode.re new file mode 100644 index 00000000..189e6a08 --- /dev/null +++ b/lib/test/cases/Ok__TwoFieldsWithSyncValidatorAndFieldWithAsyncValidatorInOnBlurMode.re @@ -0,0 +1,22 @@ +module Form = [%form + type input = { + name: [@field.async {mode: OnBlur}] string, + email: string, + age: int, + }; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name, _}) => Ok(name), + validateAsync: name => Js.Promise.resolve(Ok(name)), + }, + email: { + strategy: OnSubmit, + validate: ({email, _}) => Ok(email), + }, + age: { + strategy: OnSubmit, + validate: ({age, _}) => Ok(age), + }, + } +]; diff --git a/lib/test/cases/Ok__TwoFieldsWithSyncValidatorAndFieldWithAsyncValidatorInOnChangeMode.re b/lib/test/cases/Ok__TwoFieldsWithSyncValidatorAndFieldWithAsyncValidatorInOnChangeMode.re new file mode 100644 index 00000000..a2a416ae --- /dev/null +++ b/lib/test/cases/Ok__TwoFieldsWithSyncValidatorAndFieldWithAsyncValidatorInOnChangeMode.re @@ -0,0 +1,22 @@ +module Form = [%form + type input = { + name: [@field.async] string, + email: string, + age: int, + }; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name, _}) => Ok(name), + validateAsync: name => Js.Promise.resolve(Ok(name)), + }, + email: { + strategy: OnSubmit, + validate: ({email, _}) => Ok(email), + }, + age: { + strategy: OnSubmit, + validate: ({age, _}) => Ok(age), + }, + } +]; diff --git a/lib/test/cases/Ok__TwoFieldsWithSyncValidators.re b/lib/test/cases/Ok__TwoFieldsWithSyncValidators.re new file mode 100644 index 00000000..a84814bd --- /dev/null +++ b/lib/test/cases/Ok__TwoFieldsWithSyncValidators.re @@ -0,0 +1,16 @@ +module Form = [%form + type input = { + name: string, + age: int, + }; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name, _}) => Ok(name), + }, + age: { + strategy: OnSubmit, + validate: ({age, _}) => Ok(age), + }, + } +]; diff --git a/lib/test/dune b/lib/test/dune new file mode 100644 index 00000000..e0ce92a9 --- /dev/null +++ b/lib/test/dune @@ -0,0 +1,5 @@ +(executable + (name Test) + (public_name test.exe) + (libraries alcotest str) +) diff --git a/lib/test/script/clean b/lib/test/script/clean new file mode 100755 index 00000000..d42e24f6 --- /dev/null +++ b/lib/test/script/clean @@ -0,0 +1,3 @@ +#!/bin/bash + +find test/cases -regex ".*\.cm[itj]$" -type f -delete diff --git a/lib/test/script/print-bsc-output b/lib/test/script/print-bsc-output new file mode 100755 index 00000000..aa924be0 --- /dev/null +++ b/lib/test/script/print-bsc-output @@ -0,0 +1,5 @@ +#!/bin/bash + +source "test/script/var" + +$BSC -ppx $PPX -I $RE_FORMALITY -I $REASON_REACT -w $ERRORS -warn-error $ERRORS -bs-cmi-only $SRC diff --git a/lib/test/script/print-ppxed-source b/lib/test/script/print-ppxed-source new file mode 100755 index 00000000..1f33e1ca --- /dev/null +++ b/lib/test/script/print-ppxed-source @@ -0,0 +1,5 @@ +#!/bin/bash + +source "test/script/var" + +cat $SRC | $REFMT --parse re --print binary | $PPX /dev/stdin /dev/stdout | $REFMT --parse binary --interface false diff --git a/lib/test/script/sandbox b/lib/test/script/sandbox new file mode 100755 index 00000000..a38f7a84 --- /dev/null +++ b/lib/test/script/sandbox @@ -0,0 +1,3 @@ +#!/bin/bash + +test/script/print-ppxed-source $1 > sandbox/src/Sandbox.re diff --git a/lib/test/script/var b/lib/test/script/var new file mode 100644 index 00000000..e11dcd52 --- /dev/null +++ b/lib/test/script/var @@ -0,0 +1,12 @@ +#!/bin/bash + +SRC="./test/cases/$1.re" +SNAPSHOT="./test/cases/$1.snapshot" + +PPX="./_build/default/bin/bin.exe" +BSC="$(yarn bin)/bsc" +REFMT="$(yarn bin)/bsrefmt" +REASON_REACT="../node_modules/reason-react/lib/ocaml" +RE_FORMALITY="../node_modules/re-formality/lib/ocaml" + +ERRORS="+A" diff --git a/lib/test/script/write-error-snapshot b/lib/test/script/write-error-snapshot new file mode 100755 index 00000000..69c8ba57 --- /dev/null +++ b/lib/test/script/write-error-snapshot @@ -0,0 +1,16 @@ +#!/bin/bash + +source "test/script/var" + +$BSC -color never -ppx $PPX $SRC &> $SNAPSHOT + +RESULT=$? + +if [ $RESULT -eq 2 ] +then + echo "Output written to $SNAPSHOT" + exit 0 +else + echo "Something went wrong" + exit 1 +fi diff --git a/package.json b/package.json index e4454cb0..ba53b62a 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,18 @@ { "name": "re-formality", - "version": "3.2.0", - "description": "Reasonable form validation tool for reason-react", - "main": "src/Formality.re", + "version": "4.0.0-beta.2", + "description": "Form validation tool for reason-react", "author": "Alex Fedoseev ", "license": "MIT", - "scripts": { - "start": "parcel examples/index.html", - "build": "parcel build examples/index.html", - "prebuild": "yarn run clean && yarn run build:bs", - "build:bs": "bsb -make-world", - "clean": "yarn run clean:bs && yarn run clean:dist", - "clean:bs": "bsb -clean-world", - "clean:dist": "rm -rf dist", - "test": "exit 0", - "format": "bsrefmt --in-place **/*.{re,rei}", - "deploy": "now deploy dist --public --name re-formality", - "predeploy": "yarn run build" + "repository": { + "type": "git", + "url": "https://github.com/MinimaHQ/re-formality.git" }, - "files": [ - "src", - "bsconfig.json" + "workspaces": [ + "lib", + "specs", + "examples", + "lib/sandbox" ], "keywords": [ "react", @@ -32,18 +24,5 @@ "forms", "validation" ], - "repository": { - "type": "git", - "url": "https://github.com/alexfedoseev/re-formality.git" - }, - "devDependencies": { - "bs-platform": "7.0.1", - "bsb-js": "1.1.7", - "parcel-bundler": "1.12.4", - "re-classnames": "4.1.0", - "reason-react": "0.7.0" - }, - "dependencies": { - "re-debouncer": "2.1.0" - } + "private": true } diff --git a/specs/app/App.re b/specs/app/App.re new file mode 100644 index 00000000..f6598c97 --- /dev/null +++ b/specs/app/App.re @@ -0,0 +1,17 @@ +module Test = { + type t = + | Placeholder; + + let fromUrl = (url: ReasonReactRouter.url) => + switch (url.hash) { + | "Placeholder" => Placeholder + | _ => failwith("Nothing here") + }; +}; + +[@react.component] +let make = () => { + switch (ReasonReactRouter.useUrl()->Test.fromUrl) { + | Placeholder => + }; +}; diff --git a/specs/app/Placeholder.re b/specs/app/Placeholder.re new file mode 100644 index 00000000..869ce964 --- /dev/null +++ b/specs/app/Placeholder.re @@ -0,0 +1,49 @@ +module Form = [%form + type input = {name: string}; + let validators = { + name: { + strategy: OnSubmit, + validate: ({name}) => + switch (name) { + | "" => Error("Name is required") + | _ => Ok(name) + }, + }, + } +]; + +let initialInput: Form.input = {name: ""}; + +[@react.component] +let make = () => { + let form = Form.useForm(~initialInput, ~onSubmit=(_, _) => ()); + +
{ + event->ReactEvent.Form.preventDefault; + form.submit(); + }}> + form.blurName()} + onChange={event => { + let value = event->ReactEvent.Form.target##value; + form.updateName(_input => {name: value}); + }} + /> + {switch (form.nameResult) { + | Some(Error(message)) => +
+ message->React.string +
+ | Some(Ok(_)) => +
"OK"->React.string
+ | None => React.null + }} + +
; +}; diff --git a/specs/app/index.css b/specs/app/index.css new file mode 100644 index 00000000..1cae3442 --- /dev/null +++ b/specs/app/index.css @@ -0,0 +1,29 @@ +:root { + --font-size: 18px; +} + +* { + box-sizing: border-box; + margin: 4px; + padding: 4px; +} + +body { + display: flex; + flex-flow: row nowrap; + justify-content: center; +} + +form { + display: flex; + flex-flow: column nowrap; + width: 300px; +} + +.ok { + color: green; +} + +.error { + color: red; +} diff --git a/specs/app/index.html b/specs/app/index.html new file mode 100644 index 00000000..5f3aba99 --- /dev/null +++ b/specs/app/index.html @@ -0,0 +1,12 @@ + + + + + re-formality + + + +
+ + + diff --git a/specs/app/index.re b/specs/app/index.re new file mode 100644 index 00000000..77f75aa5 --- /dev/null +++ b/specs/app/index.re @@ -0,0 +1 @@ +ReactDOMRe.renderToElementWithId(, "app"); diff --git a/specs/bsconfig.json b/specs/bsconfig.json new file mode 100644 index 00000000..4463ea3f --- /dev/null +++ b/specs/bsconfig.json @@ -0,0 +1,19 @@ +{ + "name": "re-formality-specs", + "sources": ["app"], + "bs-dependencies": [ + "reason-react", + "re-formality" + ], + "reason": { + "react-jsx": 3 + }, + "refmt": 3, + "bsc-flags": ["-open Belt"], + "ppx-flags": ["../lib/_build/default/bin/bin.exe"], + "package-specs": { + "module": "es6", + "in-source": true + }, + "suffix": ".bs.js" +} diff --git a/specs/cypress.json b/specs/cypress.json new file mode 100644 index 00000000..aa6fdd9e --- /dev/null +++ b/specs/cypress.json @@ -0,0 +1,8 @@ +{ + "baseUrl": "http://localhost:8080", + "integrationFolder": "tests", + "screenshotsFolder": "screenshots", + "fixturesFolder": "fixtures", + "pluginsFile": "plugins/index.js", + "supportFile": "support/index.js" +} diff --git a/specs/fixtures/.gitkeep b/specs/fixtures/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/specs/package.json b/specs/package.json new file mode 100644 index 00000000..04e1a839 --- /dev/null +++ b/specs/package.json @@ -0,0 +1,24 @@ +{ + "name": "re-formality-specs", + "version": "0.0.0", + "private": true, + "scripts": { + "app:start": "parcel app/index.html --port 8080", + "bs:build": "bsb -clean-world -make-world", + "bs:watch": "bsb -make-world -w", + "bs:clean": "bsb -clean-world", + "cypress:open": "cypress open", + "cypress:run": "cypress run", + "test": "start-server-and-test app:start http://localhost:8080 cypress:run", + "pretest": "yarn run bs:build" + }, + "dependencies": { + "bs-platform": "7.2.2", + "bsb-js": "1.1.7", + "cypress": "4.1.0", + "parcel-bundler": "1.12.4", + "re-formality": "*", + "reason-react": "0.7.0", + "start-server-and-test": "1.10.10" + } +} diff --git a/specs/plugins/index.js b/specs/plugins/index.js new file mode 100644 index 00000000..aa9918d2 --- /dev/null +++ b/specs/plugins/index.js @@ -0,0 +1,21 @@ +/// +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +/** + * @type {Cypress.PluginConfig} + */ +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +} diff --git a/specs/support/commands.js b/specs/support/commands.js new file mode 100644 index 00000000..ca4d256f --- /dev/null +++ b/specs/support/commands.js @@ -0,0 +1,25 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add("login", (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/specs/support/index.js b/specs/support/index.js new file mode 100644 index 00000000..d68db96d --- /dev/null +++ b/specs/support/index.js @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands' + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/specs/tests/Placeholder.js b/specs/tests/Placeholder.js new file mode 100644 index 00000000..f0d7ece5 --- /dev/null +++ b/specs/tests/Placeholder.js @@ -0,0 +1,8 @@ +describe("Placeholder", () => { + it("shows an error", () => { + cy.visit("#Placeholder"); + cy.get("#button--submit").click(); + + cy.get("#field--name--error").should("contain", "Name is required"); + }); +}); diff --git a/src/Formality.re b/src/Formality.re deleted file mode 100644 index 6ad43a63..00000000 --- a/src/Formality.re +++ /dev/null @@ -1,21 +0,0 @@ -include Formality__Validation.Result; -include Formality__Validation.Sync; -include Formality__PublicHelpers; - -module Strategy = Formality__Strategy; -module FormStatus = Formality__FormStatus; - -module Make = Formality__Form.Make; -module MakeWithId = Formality__FormWithId.Make; - -module Async = { - include Formality__Validation.Async; - - module Make = Formality__FormAsyncOnChange.Make; - module MakeWithId = Formality__FormAsyncOnChangeWithId.Make; - - module MakeOnBlur = Formality__FormAsyncOnBlur.Make; - module MakeOnBlurWithId = Formality__FormAsyncOnBlurWithId.Make; - - let debounceInterval = Formality__FormAsyncOnChangeWithId.defaultDebounceInterval; -}; diff --git a/src/Formality.rei b/src/Formality.rei deleted file mode 100644 index e7b93919..00000000 --- a/src/Formality.rei +++ /dev/null @@ -1,63 +0,0 @@ -module Dom = Formality__PublicHelpers.Dom; -module Strategy = Formality__Strategy; -module FormStatus = Formality__FormStatus; - -module Make = Formality__Form.Make; -module MakeWithId = Formality__FormWithId.Make; - -type ok = Formality__Validation.Result.ok = | Valid | NoValue; - -type result('message) = Belt.Result.t(ok, 'message); - -type status('message) = - Formality__Validation.Sync.status('message) = - | Pristine - | Dirty( - Formality__Validation.Result.result('message), - Formality__Validation.Visibility.t, - ); - -type validate('state, 'message) = - 'state => Formality__Validation.Result.result('message); - -type validator('field, 'state, 'message) = - Formality__Validation.Sync.validator('field, 'state, 'message) = { - field: 'field, - strategy: Formality__Strategy.t, - dependents: option(list('field)), - validate: validate('state, 'message), - }; - -module Async: { - module Make = Formality__FormAsyncOnChange.Make; - module MakeWithId = Formality__FormAsyncOnChangeWithId.Make; - - module MakeOnBlur = Formality__FormAsyncOnBlur.Make; - module MakeOnBlurWithId = Formality__FormAsyncOnBlurWithId.Make; - - let debounceInterval: int; - - type status('message) = - Formality__Validation.Async.status('message) = - | Pristine - | Dirty( - Formality__Validation.Result.result('message), - Formality__Validation.Visibility.t, - ) - | Validating; - - type validate('state, 'message) = - 'state => Js.Promise.t(Formality__Validation.Result.result('message)); - - type equalityChecker('state) = ('state, 'state) => bool; - - type validator('field, 'state, 'message) = - Formality__Validation.Async.validator('field, 'state, 'message) = { - field: 'field, - strategy: Formality__Strategy.t, - dependents: option(list('field)), - validate: Formality__Validation.Sync.validate('state, 'message), - validateAsync: - option((validate('state, 'message), equalityChecker('state))), - }; -}; diff --git a/src/FormalityCompat.re b/src/FormalityCompat.re deleted file mode 100644 index 5e87ed21..00000000 --- a/src/FormalityCompat.re +++ /dev/null @@ -1,15 +0,0 @@ -include Formality__Validation.Result; -include Formality__Validation.Sync; -include Formality__PublicHelpers; - -module Strategy = Formality__Strategy; -module FormStatus = Formality__FormStatus; - -module Make = FormalityCompat__Form.Make; - -module Async = { - include Formality__Validation.Async; - module Make = FormalityCompat__FormAsyncOnChange.Make; - module MakeOnBlur = FormalityCompat__FormAsyncOnBlur.Make; - let debounceInterval = FormalityCompat__FormAsyncOnChange.defaultDebounceInterval; -}; diff --git a/src/FormalityCompat.rei b/src/FormalityCompat.rei deleted file mode 100644 index 5c00a742..00000000 --- a/src/FormalityCompat.rei +++ /dev/null @@ -1,61 +0,0 @@ -[@deprecated "Use Formality instead."]; - -module Dom = Formality__PublicHelpers.Dom; -module Strategy = Formality__Strategy; -module FormStatus = Formality__FormStatus; - -module Make = FormalityCompat__Form.Make; - -type ok = Formality__Validation.Result.ok = | Valid | NoValue; - -type result('message) = Belt.Result.t(ok, 'message); - -type status('message) = - Formality__Validation.Sync.status('message) = - | Pristine - | Dirty( - Formality__Validation.Result.result('message), - Formality__Validation.Visibility.t, - ); - -type validate('state, 'message) = - 'state => Formality__Validation.Result.result('message); - -type validator('field, 'state, 'message) = - Formality__Validation.Sync.validator('field, 'state, 'message) = { - field: 'field, - strategy: Formality__Strategy.t, - dependents: option(list('field)), - validate: validate('state, 'message), - }; - -module Async: { - module Make = FormalityCompat__FormAsyncOnChange.Make; - module MakeOnBlur = FormalityCompat__FormAsyncOnBlur.Make; - - let debounceInterval: int; - - type status('message) = - Formality__Validation.Async.status('message) = - | Pristine - | Dirty( - Formality__Validation.Result.result('message), - Formality__Validation.Visibility.t, - ) - | Validating; - - type validate('state, 'message) = - 'state => Js.Promise.t(Formality__Validation.Result.result('message)); - - type equalityChecker('state) = ('state, 'state) => bool; - - type validator('field, 'state, 'message) = - Formality__Validation.Async.validator('field, 'state, 'message) = { - field: 'field, - strategy: Formality__Strategy.t, - dependents: option(list('field)), - validate: Formality__Validation.Sync.validate('state, 'message), - validateAsync: - option((validate('state, 'message), equalityChecker('state))), - }; -}; diff --git a/src/FormalityCompat__Form.re b/src/FormalityCompat__Form.re deleted file mode 100644 index 9370972d..00000000 --- a/src/FormalityCompat__Form.re +++ /dev/null @@ -1,378 +0,0 @@ -module Validation = Formality__Validation; -module Strategy = Formality__Strategy; -module FormStatus = Formality__FormStatus; - -module type Form = { - type field; - type state; - type message; - type submissionError; - let validators: list(Validation.validator(field, state, message)); -}; - -module Make = (Form: Form) => { - module FieldId = - Id.MakeComparable({ - type t = Form.field; - let cmp = Pervasives.compare; - }); - - type state = { - input: Form.state, - status: FormStatus.t(Form.submissionError), - fields: - Map.t(Form.field, Validation.status(Form.message), FieldId.identity), - validators: - ref( - Map.t( - Form.field, - Validation.validator(Form.field, Form.state, Form.message), - FieldId.identity, - ), - ), - submittedOnce: bool, - }; - - type action = - | Change(Form.field, Form.state) - | Blur(Form.field) - | Submit - | SetSubmittedStatus(option(Form.state)) - | SetSubmissionFailedStatus(Form.submissionError) - | MapSubmissionError(Form.submissionError => Form.submissionError) - | DismissSubmissionError - | DismissSubmissionResult - | Reset; - - type interface = { - state: Form.state, - status: FormStatus.t(Form.submissionError), - result: Form.field => option(Validation.Result.result(Form.message)), - dirty: unit => bool, - valid: unit => bool, - submitting: bool, - change: (Form.field, Form.state) => unit, - blur: Form.field => unit, - submit: unit => unit, - mapSubmissionError: (Form.submissionError => Form.submissionError) => unit, - dismissSubmissionError: unit => unit, - dismissSubmissionResult: unit => unit, - reset: unit => unit, - }; - - let getInitialState = input => { - input, - status: FormStatus.Editing, - fields: - Form.validators->List.reduce( - Map.make(~id=(module FieldId)), (fields, validator) => - fields->Map.set(validator.field, Validation.Pristine) - ), - validators: - ref( - Form.validators->List.reduce( - Map.make(~id=(module FieldId)), (fields, validator) => - fields->Map.set(validator.field, validator) - ), - ), - submittedOnce: false, - }; - - let component = ReasonReact.reducerComponent("Formality.Form"); - let make = - ( - ~initialState: Form.state, - ~onSubmit: - ( - Form.state, - Validation.submissionCallbacks(Form.state, Form.submissionError) - ) => - unit, - children, - ) => { - ...component, - initialState: () => initialState->getInitialState, - reducer: (action, state) => - switch (action) { - | Change(field, input) => - let validator = (state.validators^)->Map.get(field); - switch (validator) { - | None => - ReasonReact.Update({ - ...state, - input, - fields: state.fields->Map.set(field, Dirty(Ok(Valid), Hidden)), - }) - | Some(validator) => - let status = state.fields->Map.get(field); - let result = input->(validator.validate); - let fields = - switch (validator.dependents) { - | None => state.fields - | Some(dependents) => - dependents->List.reduce( - state.fields, - (fields, field) => { - let status = fields->Map.get(field); - switch (status) { - | None - | Some(Pristine) - | Some(Dirty(_, Hidden)) => fields - | Some(Dirty(_, Shown)) => - let validator = (state.validators^)->Map.getExn(field); - fields->Map.set( - field, - Dirty(input->(validator.validate), Shown), - ); - }; - }, - ) - }; - switch (validator.strategy, status, state.submittedOnce) { - | (_, Some(Dirty(_, Shown)), _) - | (_, _, true) - | (OnFirstChange, _, false) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Shown)), - }) - | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, false) => - ReasonReact.Update({ - ...state, - input, - fields: - switch (result) { - | Ok(Valid | NoValue) => - fields->Map.set(field, Dirty(result, Shown)) - | Error(_) => fields->Map.set(field, Dirty(result, Hidden)) - }, - }) - | (OnFirstBlur | OnSubmit, _, false) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Hidden)), - }) - }; - }; - - | Blur(field) => - let status = state.fields->Map.get(field); - let validator = (state.validators^)->Map.get(field); - switch (status, validator) { - | (Some(Dirty(_, Shown)), Some(_) | None) - | (Some(Dirty(_, Hidden)), None) => ReasonReact.NoUpdate - | (Some(Pristine) | None, None) => - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(Ok(Valid), Hidden)), - }) - | (Some(Pristine) | None, Some(validator)) => - let result = state.input->(validator.validate); - switch (validator.strategy) { - | OnFirstChange - | OnFirstSuccess - | OnSubmit => - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(result, Hidden)), - }) - | OnFirstBlur - | OnFirstSuccessOrFirstBlur => - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(result, Shown)), - }) - }; - | (Some(Dirty(_, Hidden)), Some(validator)) => - let result = state.input->(validator.validate); - switch (validator.strategy) { - | OnFirstChange - | OnFirstSuccess - | OnSubmit => - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(result, Hidden)), - }) - | OnFirstBlur - | OnFirstSuccessOrFirstBlur => - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(result, Shown)), - }) - }; - }; - - | Submit => - switch (state.status) { - | Submitting(_) => ReasonReact.NoUpdate - | Editing - | Submitted - | SubmissionFailed(_) => - let (valid, fields) = - (state.validators^) - ->Map.reduce( - (true, state.fields), - ((valid, fields), field, validator) => { - let result = state.input->(validator.validate); - let fields = fields->Map.set(field, Dirty(result, Shown)); - switch (valid, result) { - | (false, _) - | (true, Error(_)) => (false, fields) - | (true, Ok(Valid | NoValue)) => (true, fields) - }; - }, - ); - if (valid) { - ReasonReact.UpdateWithSideEffects( - { - ...state, - fields, - status: - FormStatus.Submitting( - switch (state.status) { - | SubmissionFailed(error) => Some(error) - | Editing - | Submitted - | Submitting(_) => None - }, - ), - submittedOnce: true, - }, - ({state, send}) => - state.input - ->onSubmit({ - notifyOnSuccess: state => SetSubmittedStatus(state)->send, - notifyOnFailure: error => - SetSubmissionFailedStatus(error)->send, - reset: () => Reset->send, - dismissSubmissionResult: () => - DismissSubmissionResult->send, - }), - ); - } else { - ReasonReact.Update({ - ...state, - fields, - status: FormStatus.Editing, - submittedOnce: true, - }); - }; - } - - | SetSubmittedStatus(data) => - switch (data) { - | Some(data) => - ReasonReact.Update({ - ...state, - input: data, - status: FormStatus.Submitted, - fields: state.fields->Map.map(_ => Validation.Pristine), - }) - | None => - ReasonReact.Update({ - ...state, - status: FormStatus.Submitted, - fields: state.fields->Map.map(_ => Validation.Pristine), - }) - } - - | SetSubmissionFailedStatus(error) => - ReasonReact.Update({ - ...state, - status: FormStatus.SubmissionFailed(error), - }) - - | MapSubmissionError(map) => - switch (state.status) { - | Submitting(Some(error)) => - ReasonReact.Update({ - ...state, - status: Submitting(Some(error->map)), - }) - | SubmissionFailed(error) => - ReasonReact.Update({ - ...state, - status: SubmissionFailed(error->map), - }) - | Editing - | Submitting(None) - | Submitted => ReasonReact.NoUpdate - } - - | DismissSubmissionError => - switch (state.status) { - | Editing - | Submitting(_) - | Submitted => ReasonReact.NoUpdate - | SubmissionFailed(_) => - ReasonReact.Update({...state, status: FormStatus.Editing}) - } - - | DismissSubmissionResult => - switch (state.status) { - | Editing - | Submitting(_) => ReasonReact.NoUpdate - | Submitted - | SubmissionFailed(_) => - ReasonReact.Update({...state, status: FormStatus.Editing}) - } - - | Reset => ReasonReact.Update(initialState->getInitialState) - }, - - render: ({state, send}) => - children({ - state: state.input, - status: state.status, - result: field => - switch (state.fields->Map.get(field)) { - | None - | Some(Pristine) - | Some(Dirty(_, Hidden)) => None - | Some(Dirty(result, Shown)) => Some(result) - }, - dirty: () => - state.fields - ->Map.some((_, status) => - switch (status) { - | Dirty(_) => true - | Pristine => false - } - ), - valid: () => - state.fields - ->Map.every((field, status) => - switch (status) { - | Dirty(Ok(_), _) => true - | Dirty(Error(_), _) => false - | Pristine => - (state.validators^) - ->Map.get(field) - ->Option.map(validator => - switch (state.input->(validator.validate)) { - | Ok(_) => true - | Error(_) => false - } - ) - ->Option.getWithDefault(true) - } - ), - submitting: - switch (state.status) { - | Submitting(_) => true - | Editing - | Submitted - | SubmissionFailed(_) => false - }, - change: (field, state) => Change(field, state)->send, - blur: field => Blur(field)->send, - submit: () => Submit->send, - mapSubmissionError: map => MapSubmissionError(map)->send, - dismissSubmissionError: () => DismissSubmissionError->send, - dismissSubmissionResult: () => DismissSubmissionResult->send, - reset: () => Reset->send, - }), - }; -}; diff --git a/src/FormalityCompat__FormAsyncOnBlur.re b/src/FormalityCompat__FormAsyncOnBlur.re deleted file mode 100644 index 06bda16e..00000000 --- a/src/FormalityCompat__FormAsyncOnBlur.re +++ /dev/null @@ -1,489 +0,0 @@ -module Validation = Formality__Validation; -module Strategy = Formality__Strategy; -module FormStatus = Formality__FormStatus; - -module type Form = { - type field; - type state; - type message; - type submissionError; - let validators: list(Validation.Async.validator(field, state, message)); -}; - -module Make = (Form: Form) => { - module FieldId = - Id.MakeComparable({ - type t = Form.field; - let cmp = Pervasives.compare; - }); - - type state = { - input: Form.state, - status: FormStatus.t(Form.submissionError), - fields: - Map.t( - Form.field, - Validation.Async.status(Form.message), - FieldId.identity, - ), - validators: - ref( - Map.t( - Form.field, - Validation.Async.validator(Form.field, Form.state, Form.message), - FieldId.identity, - ), - ), - submittedOnce: bool, - }; - - type action = - | Change(Form.field, Form.state) - | Blur(Form.field) - | TriggerAsyncValidation( - Form.field, - Form.state, - Validation.Async.validate(Form.state, Form.message), - ) - | ApplyAsyncResult( - Form.field, - Form.state, - Validation.Result.result(Form.message), - ) - | Submit - | SetSubmittedStatus(option(Form.state)) - | SetSubmissionFailedStatus(Form.submissionError) - | MapSubmissionError(Form.submissionError => Form.submissionError) - | DismissSubmissionError - | DismissSubmissionResult - | Reset; - - type interface = { - state: Form.state, - status: FormStatus.t(Form.submissionError), - result: Form.field => option(Validation.Result.result(Form.message)), - dirty: unit => bool, - validating: Form.field => bool, - submitting: bool, - change: (Form.field, Form.state) => unit, - blur: Form.field => unit, - submit: unit => unit, - mapSubmissionError: (Form.submissionError => Form.submissionError) => unit, - dismissSubmissionError: unit => unit, - dismissSubmissionResult: unit => unit, - reset: unit => unit, - }; - - let getInitialState = input => { - input, - status: FormStatus.Editing, - fields: - Form.validators->List.reduce( - Map.make(~id=(module FieldId)), (fields, validator) => - fields->Map.set(validator.field, Validation.Async.Pristine) - ), - validators: - ref( - Form.validators->List.reduce( - Map.make(~id=(module FieldId)), (fields, validator) => - fields->Map.set(validator.field, validator) - ), - ), - submittedOnce: false, - }; - - let component = ReasonReact.reducerComponent("Formality.Form"); - let make = - ( - ~initialState: Form.state, - ~onSubmit: - ( - Form.state, - Validation.submissionCallbacks(Form.state, 'submissionError) - ) => - unit, - children, - ) => { - ...component, - initialState: () => initialState->getInitialState, - reducer: (action, state) => - switch (action) { - | Change(field, input) => - let validator = (state.validators^)->Map.get(field); - switch (validator) { - | None => - ReasonReact.Update({ - ...state, - input, - fields: state.fields->Map.set(field, Dirty(Ok(Valid), Hidden)), - }) - | Some(validator) => - let status = state.fields->Map.get(field); - let result = input->(validator.validate); - let fields = - switch (validator.dependents) { - | None => state.fields - | Some(dependents) => - dependents->List.reduce( - state.fields, - (fields, field) => { - let status = fields->Map.get(field); - switch (status) { - | None - | Some(Pristine) - | Some(Validating) - | Some(Dirty(_, Hidden)) => fields - | Some(Dirty(_, Shown)) => - let validator = (state.validators^)->Map.getExn(field); - fields->Map.set( - field, - Dirty(input->(validator.validate), Shown), - ); - }; - }, - ) - }; - switch (validator.strategy, status, state.submittedOnce) { - | (_, Some(Dirty(_, Shown)), _) - | (_, _, true) - | (OnFirstChange, _, false) => - switch (result, validator.validateAsync) { - | (_, None) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Shown)), - }) - | (Ok(Valid), Some(_)) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Hidden)), - }) - | (Ok(NoValue) | Error(_), Some(_)) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Shown)), - }) - } - - | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, false) => - switch (result, validator.validateAsync) { - | (Ok(Valid | NoValue), None) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Shown)), - }) - | (Error(_), None) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Hidden)), - }) - - | (Ok(Valid), Some(_)) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Hidden)), - }) - | (Ok(NoValue), Some(_)) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Shown)), - }) - | (Error(_), Some(_)) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Hidden)), - }) - } - - | (OnFirstBlur | OnSubmit, _, false) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Hidden)), - }) - }; - }; - - | Blur(field) => - let status = state.fields->Map.get(field); - let validator = (state.validators^)->Map.get(field); - switch (status, validator) { - | (Some(Validating), _) - | (Some(Dirty(_, Shown)), Some(_) | None) - | (Some(Dirty(_, Hidden)), None) => ReasonReact.NoUpdate - | (Some(Pristine) | None, None) => - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(Ok(Valid), Hidden)), - }) - - | (Some(Pristine | Dirty(_, Hidden)) | None, Some(validator)) => - let result = state.input->(validator.validate); - switch (validator.strategy) { - | OnFirstChange - | OnFirstSuccess - | OnSubmit => - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(result, Hidden)), - }) - | OnFirstBlur - | OnFirstSuccessOrFirstBlur => - let result = state.input->(validator.validate); - switch (result, validator.validateAsync) { - | (_, None) => - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(result, Shown)), - }) - | (Ok(Valid), Some((validateAsync, _))) => - ReasonReact.UpdateWithSideEffects( - {...state, fields: state.fields->Map.set(field, Validating)}, - ({send}) => - TriggerAsyncValidation(field, state.input, validateAsync) - ->send, - ) - | (Ok(NoValue) | Error(_), Some((_, _))) => - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(result, Shown)), - }) - }; - }; - }; - - | TriggerAsyncValidation(field, input, validateAsync) => - ReasonReact.SideEffects( - ({send}) => - Js.Promise.( - input - ->validateAsync - ->then_( - result => { - ApplyAsyncResult(field, input, result)->send; - resolve(); - }, - _, - ) - ->ignore - ), - ) - - | ApplyAsyncResult(field, input, result) => - let validator = (state.validators^)->Map.getExn(field); - let eq = validator.validateAsync->Option.getExn->snd; - if (input->eq(state.input)) { - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(result, Shown)), - }); - } else { - ReasonReact.NoUpdate; - }; - - | Submit => - switch (state.status) { - | Submitting(_) => ReasonReact.NoUpdate - | Editing - | Submitted - | SubmissionFailed(_) => - let (valid, fields, validating) = - (state.validators^) - ->Map.reduce( - (true, state.fields, false), - ((valid, fields, validating), field, validator) => { - let status = fields->Map.get(field); - switch (status) { - | _ when validating => (valid, fields, true) - | Some(Validating) => (valid, fields, true) - | Some(_) - | None => - let currentResultIsInvalid = - switch (status) { - | Some(Dirty(Error(_), _)) => true - | Some(Dirty(Ok(_), _) | Pristine | Validating) - | None => false - }; - let result = state.input->(validator.validate); - let fields = - switch ( - currentResultIsInvalid, - result, - validator.validateAsync, - ) { - | (true, Ok(Valid), Some(_)) => fields - | (_, _, _) => - fields->Map.set(field, Dirty(result, Shown)) - }; - switch (valid, fields->Map.get(field)) { - | (false, _) - | (true, Some(Dirty(Error(_), _))) => ( - false, - fields, - false, - ) - | ( - true, - Some( - Dirty(Ok(Valid | NoValue), _) | Pristine | - Validating, - ), - ) - | (true, None) => (true, fields, false) - }; - }; - }, - ); - if (validating) { - ReasonReact.NoUpdate; - } else if (valid) { - ReasonReact.UpdateWithSideEffects( - { - ...state, - fields, - status: - FormStatus.Submitting( - switch (state.status) { - | SubmissionFailed(error) => Some(error) - | Editing - | Submitted - | Submitting(_) => None - }, - ), - submittedOnce: true, - }, - ({state, send}) => - state.input - ->onSubmit({ - notifyOnSuccess: data => data->SetSubmittedStatus->send, - notifyOnFailure: error => - SetSubmissionFailedStatus(error)->send, - reset: () => Reset->send, - dismissSubmissionResult: () => - DismissSubmissionResult->send, - }), - ); - } else { - ReasonReact.Update({ - ...state, - fields, - status: FormStatus.Editing, - submittedOnce: true, - }); - }; - } - - | SetSubmittedStatus(data) => - switch (data) { - | Some(data) => - ReasonReact.Update({ - ...state, - input: data, - status: FormStatus.Submitted, - fields: state.fields->Map.map(_ => Validation.Async.Pristine), - }) - | None => - ReasonReact.Update({ - ...state, - status: FormStatus.Submitted, - fields: state.fields->Map.map(_ => Validation.Async.Pristine), - }) - } - - | SetSubmissionFailedStatus(error) => - ReasonReact.Update({ - ...state, - status: FormStatus.SubmissionFailed(error), - }) - - | MapSubmissionError(map) => - switch (state.status) { - | Submitting(Some(error)) => - ReasonReact.Update({ - ...state, - status: Submitting(Some(error->map)), - }) - | SubmissionFailed(error) => - ReasonReact.Update({ - ...state, - status: SubmissionFailed(error->map), - }) - | Editing - | Submitting(None) - | Submitted => ReasonReact.NoUpdate - } - - | DismissSubmissionError => - switch (state.status) { - | Editing - | Submitting(_) - | Submitted => ReasonReact.NoUpdate - | SubmissionFailed(_) => - ReasonReact.Update({...state, status: FormStatus.Editing}) - } - - | DismissSubmissionResult => - switch (state.status) { - | Editing - | Submitting(_) => ReasonReact.NoUpdate - | Submitted - | SubmissionFailed(_) => - ReasonReact.Update({...state, status: FormStatus.Editing}) - } - - | Reset => ReasonReact.Update(initialState->getInitialState) - }, - - render: ({state, send}) => - children({ - state: state.input, - status: state.status, - result: field => - switch (state.fields->Map.get(field)) { - | None - | Some(Pristine) - | Some(Validating) - | Some(Dirty(_, Hidden)) => None - | Some(Dirty(result, Shown)) => Some(result) - }, - dirty: () => - state.fields - ->Map.some((_, status) => - switch (status) { - | Dirty(_) - | Validating => true - | Pristine => false - } - ), - validating: field => - switch (state.fields->Map.get(field)) { - | Some(Validating) => true - | None - | Some(Pristine) - | Some(Dirty(_)) => false - }, - submitting: - switch (state.status) { - | Submitting(_) => true - | Editing - | Submitted - | SubmissionFailed(_) => false - }, - change: (field, state) => Change(field, state)->send, - blur: field => Blur(field)->send, - submit: () => Submit->send, - mapSubmissionError: map => MapSubmissionError(map)->send, - dismissSubmissionError: () => DismissSubmissionError->send, - dismissSubmissionResult: () => DismissSubmissionResult->send, - reset: () => Reset->send, - }), - }; -}; diff --git a/src/FormalityCompat__FormAsyncOnChange.re b/src/FormalityCompat__FormAsyncOnChange.re deleted file mode 100644 index 033a3a2b..00000000 --- a/src/FormalityCompat__FormAsyncOnChange.re +++ /dev/null @@ -1,551 +0,0 @@ -module Validation = Formality__Validation; -module Strategy = Formality__Strategy; -module FormStatus = Formality__FormStatus; - -let defaultDebounceInterval = 700; - -module type Form = { - type field; - type state; - type message; - type submissionError; - let debounceInterval: int; - let validators: list(Validation.Async.validator(field, state, message)); -}; - -module Make = (Form: Form) => { - module FieldId = - Id.MakeComparable({ - type t = Form.field; - let cmp = Pervasives.compare; - }); - - type state = { - input: Form.state, - status: FormStatus.t(Form.submissionError), - fields: - Map.t( - Form.field, - Validation.Async.status(Form.message), - FieldId.identity, - ), - validators: - ref(Map.t(Form.field, debouncedAsyncValidator, FieldId.identity)), - submittedOnce: bool, - } - and action = - | Change(Form.field, Form.state) - | Blur(Form.field) - | InvokeDebouncedAsyncValidation( - Form.field, - Form.state, - ( - ( - Form.field, - Form.state, - ReasonReact.self(state, ReasonReact.noRetainedProps, action), - ) - ) => - unit, - ) - | TriggerAsyncValidation( - Form.field, - Form.state, - Validation.Async.validate(Form.state, Form.message), - ) - | ApplyAsyncResult( - Form.field, - Form.state, - Validation.Result.result(Form.message), - ) - | Submit - | SetSubmittedStatus(option(Form.state)) - | SetSubmissionFailedStatus(Form.submissionError) - | MapSubmissionError(Form.submissionError => Form.submissionError) - | DismissSubmissionError - | DismissSubmissionResult - | Reset - and debouncedAsyncValidator = { - field: Form.field, - strategy: Strategy.t, - dependents: option(list(Form.field)), - validate: Validation.validate(Form.state, Form.message), - validateAsync: - option( - ( - ( - ( - Form.field, - Form.state, - ReasonReact.self(state, ReasonReact.noRetainedProps, action), - ) - ) => - unit, - Validation.Async.equalityChecker(Form.state), - ), - ), - }; - - type interface = { - state: Form.state, - status: FormStatus.t(Form.submissionError), - result: Form.field => option(Validation.Result.result(Form.message)), - dirty: unit => bool, - validating: Form.field => bool, - submitting: bool, - change: (Form.field, Form.state) => unit, - blur: Form.field => unit, - submit: unit => unit, - mapSubmissionError: (Form.submissionError => Form.submissionError) => unit, - dismissSubmissionError: unit => unit, - dismissSubmissionResult: unit => unit, - reset: unit => unit, - }; - - let debounce = (~wait, fn) => { - let fn = ((field, data, {ReasonReact.send})) => - TriggerAsyncValidation(field, data, fn)->send; - fn->(Debouncer.make(~wait)); - }; - - let getInitialState = input => { - input, - status: FormStatus.Editing, - fields: - Form.validators->List.reduce( - Map.make(~id=(module FieldId)), (fields, validator) => - fields->Map.set(validator.field, Validation.Async.Pristine) - ), - validators: - ref( - Form.validators->List.reduce( - Map.make(~id=(module FieldId)), (validators, validator) => - validators->Map.set( - validator.field, - { - field: validator.field, - strategy: validator.strategy, - dependents: validator.dependents, - validate: validator.validate, - validateAsync: - validator.validateAsync - ->Option.map(((fn, eq)) => - (fn->debounce(~wait=Form.debounceInterval), eq) - ), - }, - ) - ), - ), - submittedOnce: false, - }; - - let component = ReasonReact.reducerComponent("Formality.Form"); - let make = - ( - ~initialState: Form.state, - ~onSubmit: - ( - Form.state, - Validation.submissionCallbacks(Form.state, Form.submissionError) - ) => - unit, - children, - ) => { - ...component, - initialState: () => initialState->getInitialState, - reducer: (action, state) => - switch (action) { - | Change(field, input) => - let validator = (state.validators^)->Map.get(field); - switch (validator) { - | None => - ReasonReact.Update({ - ...state, - input, - fields: state.fields->Map.set(field, Dirty(Ok(Valid), Hidden)), - }) - | Some(validator) => - let status = state.fields->Map.get(field); - let result = input->(validator.validate); - let fields = - switch (validator.dependents) { - | None => state.fields - | Some(dependents) => - dependents->List.reduce( - state.fields, - (fields, field) => { - let status = fields->Map.get(field); - switch (status) { - | None - | Some(Pristine) - | Some(Validating) - | Some(Dirty(_, Hidden)) => fields - | Some(Dirty(_, Shown)) => - let validator = (state.validators^)->Map.getExn(field); - fields->Map.set( - field, - Dirty(input->(validator.validate), Shown), - ); - }; - }, - ) - }; - switch (validator.strategy, status, state.submittedOnce) { - | (_, Some(Dirty(_, Shown)), _) - | (_, _, true) - | (OnFirstChange, _, false) => - switch (result, validator.validateAsync) { - | (_, None) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Shown)), - }) - | (Ok(Valid), Some((validateAsync, _))) => - ReasonReact.UpdateWithSideEffects( - { - ...state, - input, - fields: fields->Map.set(field, Validating), - }, - ({send}) => - InvokeDebouncedAsyncValidation(field, input, validateAsync) - ->send, - ) - | (Ok(NoValue) | Error(_), Some(_)) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Shown)), - }) - } - - | (OnFirstSuccess | OnFirstSuccessOrFirstBlur, _, false) => - switch (result, validator.validateAsync) { - | (Ok(Valid | NoValue), None) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Shown)), - }) - | (Error(_), None) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Hidden)), - }) - - | (Ok(Valid), Some((validateAsync, _))) => - ReasonReact.UpdateWithSideEffects( - { - ...state, - input, - fields: fields->Map.set(field, Validating), - }, - ({send}) => - InvokeDebouncedAsyncValidation(field, input, validateAsync) - ->send, - ) - | (Ok(NoValue), Some(_)) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Shown)), - }) - | (Error(_), Some(_)) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Hidden)), - }) - } - - | (OnFirstBlur | OnSubmit, _, false) => - ReasonReact.Update({ - ...state, - input, - fields: fields->Map.set(field, Dirty(result, Hidden)), - }) - }; - }; - - | Blur(field) => - let status = state.fields->Map.get(field); - let validator = (state.validators^)->Map.get(field); - switch (status, validator) { - | (Some(Validating), _) - | (Some(Dirty(_, Shown)), Some(_) | None) - | (Some(Dirty(_, Hidden)), None) => ReasonReact.NoUpdate - | (Some(Pristine) | None, None) => - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(Ok(Valid), Hidden)), - }) - | (Some(Pristine | Dirty(_, Hidden)) | None, Some(validator)) => - let result = state.input->(validator.validate); - switch (validator.strategy) { - | OnFirstChange - | OnFirstSuccess - | OnSubmit => - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(result, Hidden)), - }) - | OnFirstBlur - | OnFirstSuccessOrFirstBlur => - switch (result, validator.validateAsync) { - | (_, None) => - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(result, Shown)), - }) - | (Ok(Valid), Some((validateAsync, _))) => - ReasonReact.UpdateWithSideEffects( - {...state, fields: state.fields->Map.set(field, Validating)}, - ({send}) => - InvokeDebouncedAsyncValidation( - field, - state.input, - validateAsync, - ) - ->send, - ) - | (Ok(NoValue) | Error(_), Some(_)) => - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(result, Shown)), - }) - } - }; - }; - - | InvokeDebouncedAsyncValidation(field, input, validateAsync) => - ReasonReact.SideEffects(self => (field, input, self)->validateAsync) - - | TriggerAsyncValidation(field, input, validateAsync) => - ReasonReact.SideEffects( - ({send}) => - Js.Promise.( - input - ->validateAsync - ->then_( - result => { - ApplyAsyncResult(field, input, result)->send; - resolve(); - }, - _, - ) - ->ignore - ), - ) - - | ApplyAsyncResult(field, input, result) => - let validator = (state.validators^)->Map.getExn(field); - let eq = validator.validateAsync->Option.getExn->snd; - if (input->eq(state.input)) { - ReasonReact.Update({ - ...state, - fields: state.fields->Map.set(field, Dirty(result, Shown)), - }); - } else { - ReasonReact.NoUpdate; - }; - - | Submit => - switch (state.status) { - | Submitting(_) => ReasonReact.NoUpdate - | Editing - | Submitted - | SubmissionFailed(_) => - let (valid, fields, validating) = - (state.validators^) - ->Map.reduce( - (true, state.fields, false), - ((valid, fields, validating), field, validator) => { - let status = fields->Map.get(field); - switch (status) { - | _ when validating => (valid, fields, true) - | Some(Validating) => (valid, fields, true) - | Some(_) - | None => - let currentResultIsInvalid = - switch (status) { - | Some(Dirty(Error(_), _)) => true - | Some(Dirty(Ok(_), _) | Pristine | Validating) - | None => false - }; - let result = state.input->(validator.validate); - let fields = - switch ( - currentResultIsInvalid, - result, - validator.validateAsync, - ) { - | (true, Ok(Valid), Some(_)) => fields - | (_, _, _) => - fields->Map.set(field, Dirty(result, Shown)) - }; - switch (valid, fields->Map.get(field)) { - | (false, _) - | (true, Some(Dirty(Error(_), _))) => ( - false, - fields, - false, - ) - | ( - true, - Some( - Dirty(Ok(Valid | NoValue), _) | Pristine | - Validating, - ), - ) - | (true, None) => (true, fields, false) - }; - }; - }, - ); - if (validating) { - ReasonReact.NoUpdate; - } else if (valid) { - ReasonReact.UpdateWithSideEffects( - { - ...state, - fields, - status: - FormStatus.Submitting( - switch (state.status) { - | SubmissionFailed(error) => Some(error) - | Editing - | Submitted - | Submitting(_) => None - }, - ), - submittedOnce: true, - }, - ({state, send}) => - state.input - ->onSubmit({ - notifyOnSuccess: data => data->SetSubmittedStatus->send, - notifyOnFailure: error => - SetSubmissionFailedStatus(error)->send, - reset: () => Reset->send, - dismissSubmissionResult: () => - DismissSubmissionResult->send, - }), - ); - } else { - ReasonReact.Update({ - ...state, - fields, - status: FormStatus.Editing, - submittedOnce: true, - }); - }; - } - - | SetSubmittedStatus(data) => - switch (data) { - | Some(data) => - ReasonReact.Update({ - ...state, - input: data, - status: FormStatus.Submitted, - fields: state.fields->Map.map(_ => Validation.Async.Pristine), - }) - | None => - ReasonReact.Update({ - ...state, - status: FormStatus.Submitted, - fields: state.fields->Map.map(_ => Validation.Async.Pristine), - }) - } - - | SetSubmissionFailedStatus(error) => - ReasonReact.Update({ - ...state, - status: FormStatus.SubmissionFailed(error), - }) - - | MapSubmissionError(map) => - switch (state.status) { - | Submitting(Some(error)) => - ReasonReact.Update({ - ...state, - status: Submitting(Some(error->map)), - }) - | SubmissionFailed(error) => - ReasonReact.Update({ - ...state, - status: SubmissionFailed(error->map), - }) - | Editing - | Submitting(None) - | Submitted => ReasonReact.NoUpdate - } - - | DismissSubmissionError => - switch (state.status) { - | Editing - | Submitting(_) - | Submitted => ReasonReact.NoUpdate - | SubmissionFailed(_) => - ReasonReact.Update({...state, status: FormStatus.Editing}) - } - - | DismissSubmissionResult => - switch (state.status) { - | Editing - | Submitting(_) => ReasonReact.NoUpdate - | Submitted - | SubmissionFailed(_) => - ReasonReact.Update({...state, status: FormStatus.Editing}) - } - - | Reset => ReasonReact.Update(initialState->getInitialState) - }, - - render: ({state, send}) => - children({ - state: state.input, - status: state.status, - result: field => - switch (state.fields->Map.get(field)) { - | None - | Some(Pristine) - | Some(Validating) - | Some(Dirty(_, Hidden)) => None - | Some(Dirty(result, Shown)) => Some(result) - }, - dirty: () => - state.fields - ->Map.some((_, status) => - switch (status) { - | Dirty(_) - | Validating => true - | Pristine => false - } - ), - validating: field => - switch (state.fields->Map.get(field)) { - | Some(Validating) => true - | None - | Some(Pristine) - | Some(Dirty(_)) => false - }, - submitting: - switch (state.status) { - | Submitting(_) => true - | Editing - | Submitted - | SubmissionFailed(_) => false - }, - change: (field, state) => Change(field, state)->send, - blur: field => Blur(field)->send, - submit: () => Submit->send, - mapSubmissionError: map => MapSubmissionError(map)->send, - dismissSubmissionError: () => DismissSubmissionError->send, - dismissSubmissionResult: () => DismissSubmissionResult->send, - reset: () => Reset->send, - }), - }; -}; diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 91ca51bb..00000000 --- a/webpack.config.js +++ /dev/null @@ -1,11 +0,0 @@ -const path = require('path'); - -module.exports = { - entry: { - app: './examples/index.bs.js', - }, - output: { - path: path.join(__dirname, "examples", "build"), - filename: '[name].js', - }, -}; diff --git a/yarn.lock b/yarn.lock index 3f80d8c7..95ae31f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,14 +2,7 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" - integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== - dependencies: - "@babel/highlight" "^7.0.0" - -"@babel/code-frame@^7.5.5": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== @@ -164,14 +157,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== -"@babel/helper-regex@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.0.0.tgz#2c1718923b57f9bbe64705ffe5640ac64d9bdb27" - integrity sha512-TR0/N0NDCcUIUEbqV6dCO+LptmmSQFQ7q70lfcEB4URsjD0E1HzicrwUH+ap6BAQ2jhCX9Q4UqZy4wilujWlkg== - dependencies: - lodash "^4.17.10" - -"@babel/helper-regex@^7.4.4": +"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351" integrity sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw== @@ -234,9 +220,9 @@ "@babel/types" "^7.7.4" "@babel/highlight@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" - integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw== + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" + integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ== dependencies: chalk "^2.0.0" esutils "^2.0.2" @@ -710,6 +696,62 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@cypress/listr-verbose-renderer@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a" + integrity sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo= + dependencies: + chalk "^1.1.3" + cli-cursor "^1.0.2" + date-fns "^1.27.2" + figures "^1.7.0" + +"@cypress/xvfb@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" + integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== + dependencies: + debug "^3.1.0" + lodash.once "^4.1.1" + +"@hapi/address@^2.1.2": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" + integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ== + +"@hapi/formula@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@hapi/formula/-/formula-1.2.0.tgz#994649c7fea1a90b91a0a1e6d983523f680e10cd" + integrity sha512-UFbtbGPjstz0eWHb+ga/GM3Z9EzqKXFWIbSOFURU0A/Gku0Bky4bCk9/h//K2Xr3IrCfjFNhMm4jyZ5dbCewGA== + +"@hapi/hoek@^8.2.4", "@hapi/hoek@^8.3.0": + version "8.5.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" + integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== + +"@hapi/joi@^16.1.8": + version "16.1.8" + resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-16.1.8.tgz#84c1f126269489871ad4e2decc786e0adef06839" + integrity sha512-wAsVvTPe+FwSrsAurNt5vkg3zo+TblvC5Bb1zMVK6SJzZqw9UrJnexxR+76cpePmtUZKHAPxcQ2Bf7oVHyahhg== + dependencies: + "@hapi/address" "^2.1.2" + "@hapi/formula" "^1.2.0" + "@hapi/hoek" "^8.2.4" + "@hapi/pinpoint" "^1.0.2" + "@hapi/topo" "^3.1.3" + +"@hapi/pinpoint@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@hapi/pinpoint/-/pinpoint-1.0.2.tgz#025b7a36dbbf4d35bf1acd071c26b20ef41e0d13" + integrity sha512-dtXC/WkZBfC5vxscazuiJ6iq4j9oNx1SHknmIr8hofarpKUZKmlUVYVIhNVzIEgK5Wrc4GMHL5lZtt1uS2flmQ== + +"@hapi/topo@^3.1.3": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29" + integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ== + dependencies: + "@hapi/hoek" "^8.3.0" + "@iarna/toml@^2.2.0": version "2.2.3" resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.3.tgz#f060bf6eaafae4d56a7dac618980838b0696e2ab" @@ -723,10 +765,10 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@nodelib/fs.stat@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.2.tgz#54c5a964462be3d4d78af631363c18d6fa91ac26" - integrity sha512-yprFYuno9FtNsSHVlSWd+nRlmGoAbqbeCwOryP6sC/zoCjhpArcRMYp19EvpSUSizJAlsXEwJv+wcWS9XaXdMw== +"@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" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== "@parcel/fs@^1.11.0": version "1.11.0" @@ -769,38 +811,55 @@ "@parcel/utils" "^1.11.0" physical-cpu-count "^2.0.0" +"@samverschueren/stream-to-observable@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" + integrity sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg== + dependencies: + any-observable "^0.3.0" + +"@types/q@^1.5.1": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" + integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== + +"@types/sizzle@2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" + integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== + abab@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f" - integrity sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w== + version "2.0.3" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" + integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== -acorn-globals@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.0.tgz#e3b6f8da3c1552a95ae627571f7dd6923bb54103" - integrity sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw== +acorn-globals@^4.3.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" + integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== dependencies: acorn "^6.0.1" acorn-walk "^6.0.1" acorn-walk@^6.0.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913" - integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw== + version "6.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" + integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== -acorn@^5.0.0, acorn@^5.5.3: +acorn@^5.0.0: version "5.7.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== -acorn@^6.0.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" - integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== +acorn@^6.0.1, acorn@^6.0.4: + version "6.4.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" + integrity sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw== ajv@^6.5.5: - version "6.10.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" - integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== dependencies: fast-deep-equal "^2.0.1" fast-json-stable-stringify "^2.0.0" @@ -812,6 +871,11 @@ alphanum-sort@^1.0.0: resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= +ansi-escapes@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -822,12 +886,17 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -835,11 +904,16 @@ ansi-styles@^3.2.1: color-convert "^1.9.0" ansi-to-html@^0.6.4: - version "0.6.6" - resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.6.6.tgz#58a8d04b87ec9a85e3ad273c12a5fbc7147b9c42" - integrity sha512-90M/2sZna3OsoOEbSyXK46poFnlClBC53Rx6etNKQK7iShsX5fI5E/M9Ld6FurtLaxAWLuAPi0Jp8p3y5oAkxg== + version "0.6.13" + resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.6.13.tgz#c72eae8b63e5ca0643aab11bfc6e6f2217425833" + integrity sha512-Ys2/umuaTlQvP9DLkaa7UzRKF2FLrfod/hNHXS9QhXCrw7seObG6ksOGmNz3UoK+adwM8L9vQfG7mvaxfJ3Jvw== dependencies: - entities "^1.1.1" + entities "^1.1.2" + +any-observable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" + integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== anymatch@^2.0.0: version "2.0.0" @@ -849,6 +923,11 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +arch@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e" + integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -903,10 +982,11 @@ assert-plus@1.0.0, assert-plus@^1.0.0: integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= assert@^1.1.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" - integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE= + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== dependencies: + object-assign "^4.1.1" util "0.10.3" assign-symbols@^1.0.0: @@ -920,16 +1000,21 @@ async-each@^1.0.1: integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== async-limiter@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" - integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" + integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -atob@^2.1.1: +atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== @@ -940,9 +1025,9 @@ aws-sign2@~0.7.0: integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" - integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== + version "1.9.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.0.tgz#24390e6ad61386b0a747265754d2a17219de862c" + integrity sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A== babel-plugin-dynamic-import-node@^2.3.0: version "2.3.0" @@ -984,9 +1069,9 @@ balanced-match@^1.0.0: integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= base64-js@^1.0.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" - integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== base@^0.11.1: version "0.11.2" @@ -1009,9 +1094,9 @@ bcrypt-pbkdf@^1.0.0: tweetnacl "^0.14.3" binary-extensions@^1.0.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14" - integrity sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg== + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== bindings@^1.5.0: version "1.5.0" @@ -1020,10 +1105,10 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -bindings@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" - integrity sha1-FK1hE4EtLTfXLme0ystLtyZQXxE= +bluebird@3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" @@ -1138,43 +1223,30 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.1.2.tgz#632feb46d1cbdd6bb1a6eb660eff68f2345ae7e7" - integrity sha512-docXmVcYth9AiW5183dEe2IxnbmpXF4jiM6efGBVRAli/iDSS894Svvjenrv5NPqAJ4dEJULmT4MSvmLG9qoYg== - dependencies: - caniuse-lite "^1.0.30000888" - electron-to-chromium "^1.3.73" - node-releases "^1.0.0-alpha.12" - -browserslist@^4.1.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.3.4.tgz#4477b737db6a1b07077275b24791e680d4300425" - integrity sha512-u5iz+ijIMUlmV8blX82VGFrB9ecnUg5qEt55CMZ/YJEhha+d8qpBfOFuutJ6F/VKRXjZoD33b6uvarpPxcl3RA== - dependencies: - caniuse-lite "^1.0.30000899" - electron-to-chromium "^1.3.82" - node-releases "^1.0.1" - -browserslist@^4.6.0, browserslist@^4.8.2: - version "4.8.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.2.tgz#b45720ad5fbc8713b7253c20766f701c9a694289" - integrity sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA== +browserslist@^4.0.0, browserslist@^4.1.0, browserslist@^4.6.0, browserslist@^4.8.3: + version "4.8.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.3.tgz#65802fcd77177c878e015f0e3189f2c4f627ba44" + integrity sha512-iU43cMMknxG1ClEZ2MDKeonKE1CCrFVkQK2AqO2YWFmvIrx4JWrvQ4w4hQez6EpVI8rHTtqh/ruHHDHSOKxvUg== dependencies: - caniuse-lite "^1.0.30001015" + caniuse-lite "^1.0.30001017" electron-to-chromium "^1.3.322" - node-releases "^1.1.42" + node-releases "^1.1.44" -bs-platform@7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-7.0.1.tgz#1d7b0ef6088b998dceee5db74a7cd8f01c20a3bd" - integrity sha512-UjStdtHhbtC/l6vKJ1XRDqrPk7rFf5PLYHtRX3akDXEYVnTbN36z0g4DEr5mU8S0N945e33HYts9x+i7hKKpZQ== +bs-platform@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-7.2.2.tgz#76fdc63e4889458ae3d257a0132107a792f2309c" + integrity sha512-PWcFfN+jCTtT/rMaHDhKh+W9RUTpaRunmSF9vbLYcrJbpgCNW6aFKAY33u0P3mLxwuhshN3b4FxqGUBPj6exZQ== bsb-js@1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/bsb-js/-/bsb-js-1.1.7.tgz#12cc91e974f5896b3a2aa8358419d24e56f552c3" integrity sha512-FSR7d5Kb6KfVI5sLz9bEeD9pbA9F4Zbkk1xa6R9CJtyd+dJ7mIkxoXqv7cw7HWcuee+VpIhmjYzrplnFd/5Qgg== +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + buffer-equal@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" @@ -1191,9 +1263,9 @@ buffer-xor@^1.0.3: integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= buffer@^4.3.0: - version "4.9.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" - integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg= + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" @@ -1219,11 +1291,40 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cachedir@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" + integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== + call-me-maybe@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" @@ -1234,27 +1335,26 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000888: - version "1.0.30000889" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000889.tgz#53e266c83e725ad3bd2e4a3ea76d5031a8aa4c3e" - integrity sha512-MFxcQ6x/LEEoaIhO7Zdb7Eg8YyNONN+WBnS5ERJ0li2yRw51+i4xXUNxnLaveTb/4ZoJqsWKEmlomhG2pYzlQA== - -caniuse-lite@^1.0.30000899: - version "1.0.30000907" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000907.tgz#0b9899bde53fb1c30e214fb12402361e02ff5c42" - integrity sha512-No5sQ/OB2Nmka8MNOOM6nJx+Hxt6MQ6h7t7kgJFu9oTuwjykyKRSBP/+i/QAyFHxeHB+ddE0Da1CG5ihx9oehQ== - -caniuse-lite@^1.0.30001015: - version "1.0.30001016" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001016.tgz#16ea48d7d6e8caf3cad3295c2d746fe38c4e7f66" - integrity sha512-yYQ2QfotceRiH4U+h1Us86WJXtVHDmy3nEKIdYPsZCYnOV5/tMgGbmoIlrMzmh2VXlproqYtVaKeGDBkMZifFA== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001017: + version "1.0.30001020" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001020.tgz#3f04c1737500ffda78be9beb0b5c1e2070e15926" + integrity sha512-yWIvwA68wRHKanAVS1GjN8vajAv7MBFshullKCeq/eKpK7pJBVDgFFEqvgWTkcP2+wIDeQGYFRXECjKZnLkUjA== caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@^1.1.3: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= @@ -1265,23 +1365,10 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" - integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" +check-more-types@2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= chokidar@^2.1.5: version "2.1.8" @@ -1302,6 +1389,11 @@ chokidar@^2.1.5: optionalDependencies: fsevents "^1.2.7" +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + 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" @@ -1320,7 +1412,14 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -cli-cursor@^2.1.0: +cli-cursor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= + dependencies: + restore-cursor "^1.0.1" + +cli-cursor@^2.0.0, cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= @@ -1332,6 +1431,23 @@ cli-spinners@^1.1.0: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg== +cli-truncate@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" + integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= + dependencies: + slice-ansi "0.0.4" + string-width "^1.0.1" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -1342,13 +1458,20 @@ clone@^2.1.1: resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= -coa@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.1.tgz#f3f8b0b15073e35d70263fb1042cb2c023db38af" - integrity sha512-5wfTTO8E2/ja4jFSxePXlG5nRu5bBtL/r1HCIpJW/lzT6yDtKl0u0Z4o/Vpz32IpKmBn7HerheEZQgA9N2DarQ== +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" q "^1.1.2" +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -1383,56 +1506,51 @@ color-string@^1.5.2: simple-swizzle "^0.2.2" color@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/color/-/color-3.0.0.tgz#d920b4328d534a3ac8295d68f7bd4ba6c427be9a" - integrity sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w== + version "3.1.2" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" + integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== dependencies: color-convert "^1.9.1" color-string "^1.5.2" -colors@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" - integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM= - combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" - integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" command-exists@^1.2.6: - version "1.2.7" - resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.7.tgz#16828f0c3ff2b0c58805861ef211b64fc15692a8" - integrity sha512-doWDvhXCcW5LK0cIUWrOQ8oMFXJv3lEQCkJpGVjM8v9SV0uhqYXB943538tEA2CiaWqSyuYUGAm5ezDwEx9xlw== + version "1.2.8" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.8.tgz#715acefdd1223b9c9b37110a149c6392c2852291" + integrity sha512-PM54PkseWbiiD/mMsbvW351/u+dafwTJ0ye2qB60G1aGQP9j3xK2gmMDc+R34L3nDtx4qMCitXT75mkbkGJDLw== -commander@^2.11.0: - version "2.18.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" - integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== +commander@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.0.tgz#545983a0603fe425bc672d66c9e3c89c42121a83" + integrity sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw== -commander@^2.19.0, commander@^2.9.0: - version "2.20.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" - integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== +commander@^2.11.0, commander@^2.19.0, commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@~2.17.1: - version "2.17.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" - integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== +common-tags@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" + integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== component-emitter@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" - integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@~1.6.0: +concat-stream@1.6.2, concat-stream@~1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -1443,25 +1561,16 @@ concat-stream@~1.6.0: typedarray "^0.0.6" console-browserify@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" - integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA= - dependencies: - date-now "^0.1.4" + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= -convert-source-map@^1.5.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" - integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== - dependencies: - safe-buffer "~5.1.1" - -convert-source-map@^1.7.0: +convert-source-map@^1.5.1, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -1474,19 +1583,14 @@ copy-descriptor@^0.1.0: integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-js-compat@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.0.tgz#4eb6cb69d03d99159ed7c860cd5fcf7d23a62ea9" - integrity sha512-Z3eCNjGgoYluH89Jt4wVkfYsc/VdLrA2/woX5lm0isO/pCT+P+Y+o65bOuEnjDJLthdwTBxbCVzptTXtc18fJg== + version "3.6.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.2.tgz#314ca8b84d5e71c27c19f1ecda966501b1cf1f10" + integrity sha512-+G28dzfYGtAM+XGvB1C5AS1ZPKfQ47HLhcdeIQdZgQnJVdp7/D0m+W/TErwhgsX6CujRUk/LebB6dCrKrtJrvQ== dependencies: - browserslist "^4.8.2" + browserslist "^4.8.3" semver "7.0.0" -core-js@^2.4.0: - version "2.5.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" - integrity sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw== - -core-js@^2.6.5: +core-js@^2.4.0, core-js@^2.6.5: version "2.6.11" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== @@ -1497,12 +1601,13 @@ core-util-is@1.0.2, core-util-is@~1.0.0: integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= cosmiconfig@^5.0.0: - version "5.0.6" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.6.tgz#dca6cf680a0bd03589aff684700858c81abeeb39" - integrity sha512-6DWfizHriCrFWURP1/qyhsiFvYdlJzbCzmtFWh744+KyWsJo5+kPzUZZaMRSSItoYc0pxFX7gEO7ZC1/gN/7AQ== + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== dependencies: + import-fresh "^2.0.0" is-directory "^0.3.1" - js-yaml "^3.9.0" + js-yaml "^3.13.1" parse-json "^4.0.0" create-ecdh@^4.0.0: @@ -1536,7 +1641,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@^6.0.4: +cross-spawn@^6.0.0, cross-spawn@^6.0.4: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -1547,6 +1652,15 @@ cross-spawn@^6.0.4: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" + integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -1589,20 +1703,20 @@ css-modules-loader-core@^1.1.0: postcss-modules-scope "1.1.0" postcss-modules-values "1.3.0" -css-select-base-adapter@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.0.tgz#0102b3d14630df86c3eb9fa9f5456270106cf990" - integrity sha1-AQKz0UYw34bD65+p9UVicBBs+ZA= +css-select-base-adapter@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== css-select@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.0.0.tgz#7aa2921392114831f68db175c0b6a555df74bbd5" - integrity sha512-MGhoq1S9EyPgZIGnts8Yz5WwUOyHmPMdlqeifsYs/xFX7AAm3hY0RJe1dqVlXtYPI66Nsk39R/sa5/ree6L2qg== + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" + integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== dependencies: boolbase "^1.0.0" - css-what "2.1" + css-what "^3.2.1" domutils "^1.7.0" - nth-check "^1.0.1" + nth-check "^1.0.2" css-selector-tokenizer@^0.7.0: version "0.7.1" @@ -1613,36 +1727,23 @@ 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" - integrity sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w== - 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" - integrity sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg== +css-tree@1.0.0-alpha.37: + version "1.0.0-alpha.37" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== dependencies: - mdn-data "~1.1.0" - source-map "^0.5.3" + mdn-data "2.0.4" + source-map "^0.6.1" 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" integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY= -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" - integrity sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w= - -css-what@2.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" - integrity sha1-lGfQMsOM+u+58teVASUwYvh/ob0= +css-what@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1" + integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw== cssesc@^0.1.0: version "0.1.0" @@ -1654,41 +1755,10 @@ cssesc@^2.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== -cssnano-preset-default@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.2.tgz#1de3f27e73b7f0fbf87c1d7fd7a63ae980ac3774" - integrity sha512-zO9PeP84l1E4kbrdyF7NSLtA/JrJY1paX5FHy5+w/ziIXO2kDqDMfJ/mosXkaHHSa3RPiIY3eB6aEgwx3IiGqA== - dependencies: - css-declaration-sorter "^4.0.1" - cssnano-util-raw-cache "^4.0.1" - postcss "^7.0.0" - postcss-calc "^6.0.2" - 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.6" - 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" +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== cssnano-preset-default@^4.0.7: version "4.0.7" @@ -1748,17 +1818,7 @@ cssnano-util-same-parent@^4.0.0: resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== -cssnano@^4.0.0: - version "4.1.4" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.4.tgz#55b71e3d8f5451dd3edc7955673415c98795788f" - integrity sha512-wP0wbOM9oqsek14CiNRYrK9N3w3jgadtGZKHXysgC/OMVpy0KZgWVPdNqODSZbz7txO9Gekr9taOfcCgL0pOOw== - dependencies: - cosmiconfig "^5.0.0" - cssnano-preset-default "^4.0.2" - is-resolvable "^1.0.0" - postcss "^7.0.0" - -cssnano@^4.1.9: +cssnano@^4.0.0, cssnano@^4.1.10: version "4.1.10" resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== @@ -1768,25 +1828,66 @@ cssnano@^4.1.9: 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" - integrity sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg== +csso@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.2.tgz#e5f81ab3a56b8eefb7f0092ce7279329f454de3d" + integrity sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg== dependencies: - css-tree "1.0.0-alpha.29" + css-tree "1.0.0-alpha.37" -cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": - version "0.3.6" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.6.tgz#f85206cee04efa841f3c5982a74ba96ab20d65ad" - integrity sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A== +cssom@0.3.x, cssom@^0.3.4: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== -cssstyle@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.2.2.tgz#427ea4d585b18624f6fdbf9de7a2a1a3ba713077" - integrity sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow== +cssstyle@^1.1.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" + integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== dependencies: cssom "0.3.x" +cypress@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-4.1.0.tgz#295f115d2e8a08fff2760ab49d94d876f5877aee" + integrity sha512-FFV8pS9iuriSX4M9rna6awJUhiqozZD1D5z5BprCUJoho1ctbcgpkEUIUnqxli2OwjQqVz07egO+iqoGL+tw7g== + dependencies: + "@cypress/listr-verbose-renderer" "0.4.1" + "@cypress/xvfb" "1.2.4" + "@types/sizzle" "2.3.2" + arch "2.1.1" + bluebird "3.7.2" + cachedir "2.3.0" + chalk "2.4.2" + check-more-types "2.24.0" + commander "4.1.0" + common-tags "1.8.0" + debug "4.1.1" + eventemitter2 "4.1.2" + execa "1.0.0" + executable "4.1.1" + extract-zip "1.6.7" + fs-extra "8.1.0" + getos "3.1.4" + is-ci "2.0.0" + is-installed-globally "0.1.0" + lazy-ass "1.6.0" + listr "0.14.3" + lodash "4.17.15" + log-symbols "3.0.0" + minimist "1.2.0" + moment "2.24.0" + ospath "1.2.2" + pretty-bytes "5.3.0" + ramda "0.26.1" + request "2.88.0" + request-progress "3.0.0" + supports-color "7.1.0" + tmp "0.1.0" + untildify "4.0.0" + url "0.11.0" + yauzl "2.10.0" + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1794,7 +1895,7 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-urls@^1.0.0: +data-urls@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== @@ -1803,18 +1904,18 @@ data-urls@^1.0.0: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" -date-now@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" - integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= +date-fns@^1.27.2: + version "1.30.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" + integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== deasync@^0.1.14: - version "0.1.14" - resolved "https://registry.yarnpkg.com/deasync/-/deasync-0.1.14.tgz#232ea2252b443948cad033d792eb3b24b0a3d828" - integrity sha512-wN8sIuEqIwyQh72AG7oY6YQODCxIp1eXzEZlZznBuwDF8Q03Tdy9QNp1BNZXeadXoklNrw+Ip1fch+KXo/+ASw== + version "0.1.19" + resolved "https://registry.yarnpkg.com/deasync/-/deasync-0.1.19.tgz#e7ea89fcc9ad483367e8a48fe78f508ca86286e8" + integrity sha512-oh3MRktfnPlLysCPpBpKZZzb4cUC/p0aA3SyRGp15lN30juJBTo/CiD0d4fR+f1kBtUQoJj1NE9RPNWQ7BQ9Mg== dependencies: - bindings "~1.2.1" - node-addon-api "^1.6.0" + bindings "^1.5.0" + node-addon-api "^1.7.1" debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" @@ -1823,13 +1924,25 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87" - integrity sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg== +debug@4.1.1, debug@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== dependencies: ms "^2.1.1" +debug@^3.1.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -1847,7 +1960,7 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-properties@^1.1.2: +define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== @@ -1887,9 +2000,9 @@ depd@~1.1.2: integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= des.js@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" - integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw= + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== dependencies: inherits "^2.0.1" minimalistic-assert "^1.0.0" @@ -1909,27 +2022,27 @@ diffie-hellman@^5.0.0: randombytes "^2.0.0" dom-serializer@0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" - integrity sha1-BzxpdUbOB4DOI75KKOKT5AvDDII= + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== dependencies: - domelementtype "~1.1.1" - entities "~1.1.1" + domelementtype "^2.0.1" + entities "^2.0.0" domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -domelementtype@1, domelementtype@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" - integrity sha1-sXrtguirWeUt2cGbF1bg/BhyBMI= +domelementtype@1, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== -domelementtype@~1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" - integrity sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs= +domelementtype@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" + integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== domexception@^1.0.1: version "1.0.1" @@ -1977,6 +2090,11 @@ duplexer2@~0.1.4: dependencies: readable-stream "^2.0.2" +duplexer@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -1991,24 +2109,19 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.322: - version "1.3.322" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz#a6f7e1c79025c2b05838e8e344f6e89eb83213a8" - integrity sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA== - -electron-to-chromium@^1.3.73: - version "1.3.73" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.73.tgz#aa67787067d58cc3920089368b3b8d6fe0fc12f6" - integrity sha512-6PIg7v9zRoVGh6EheRF8h6Plti+3Yo/qtHobS4/Htyt53DNHmKKGFqSae1AIk0k1S4gCQvt7I2WgpbuZNcDY+g== + version "1.3.329" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.329.tgz#ff3644fb216bdccc33b2063c36f679deb5316cde" + integrity sha512-CoyYGbkQLwmOpaWRUZgeSNnEPH5fE5R8T7dhQIWV/rlIt+Kx6NFppQJ2oHELmzw8ZGabOBY5CrjGjyA+74QVoQ== -electron-to-chromium@^1.3.82: - version "1.3.84" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.84.tgz#2e55df59e818f150a9f61b53471ebf4f0feecc65" - integrity sha512-IYhbzJYOopiTaNWMBp7RjbecUBsbnbDneOP86f3qvS0G0xfzwNSvMJpTrvi5/Y1gU7tg2NAgeg8a8rCYvW9Whw== +elegant-spinner@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" + integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= elliptic@^6.0.0: - version "6.4.1" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a" - integrity sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ== + version "6.5.2" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" + integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw== dependencies: bn.js "^4.4.0" brorand "^1.0.1" @@ -2018,15 +2131,32 @@ elliptic@^6.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -entities@^1.1.1, entities@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" - integrity sha1-blwtClYhtdra7O+AuQ7ftc13cvA= +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +entities@^1.1.1, entities@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" + integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== envinfo@^7.3.1: version "7.5.0" @@ -2040,21 +2170,27 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.5.1, es-abstract@^1.6.1: - version "1.12.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" - integrity sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA== +es-abstract@^1.17.0-next.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.0.tgz#f42a517d0036a5591dbb2c463591dc8bb50309b1" + integrity sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug== dependencies: - es-to-primitive "^1.1.1" + es-to-primitive "^1.2.1" function-bind "^1.1.1" - has "^1.0.1" - is-callable "^1.1.3" - is-regex "^1.0.4" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.1.5" + is-regex "^1.0.5" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimleft "^2.1.1" + string.prototype.trimright "^2.1.1" -es-to-primitive@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" - integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" is-date-object "^1.0.1" @@ -2070,22 +2206,10 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escodegen@^1.8.1: - version "1.11.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589" - integrity sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw== - dependencies: - esprima "^3.1.3" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -escodegen@^1.9.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.1.tgz#c485ff8d6b4cdb89e27f4a856e91f118401ca510" - integrity sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw== +escodegen@^1.11.0, escodegen@^1.11.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.1.tgz#08770602a74ac34c7a90ca9229e7d51e379abc76" + integrity sha512-Q8t2YZ+0e0pc7NRVj3B4tSQ9rim1oi4Fh46k2xhJ2qOiEwhQfdjyEQddWdj7ZFaKmU+5104vn1qrcjEPWq+bgQ== dependencies: esprima "^3.1.3" estraverse "^4.2.0" @@ -2117,24 +2241,42 @@ esprima@^4.0.0: integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== estraverse@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== esutils@^2.0.0, esutils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" - integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -events@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" - integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + +eventemitter2@4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-4.1.2.tgz#0e1a8477af821a6ef3995b311bf74c23a5247f15" + integrity sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU= + +events@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" + integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg== evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" @@ -2144,6 +2286,47 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +execa@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + 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" + +execa@3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" + integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + p-finally "^2.0.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +executable@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= + expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -2191,6 +2374,16 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +extract-zip@1.6.7: + version "1.6.7" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9" + integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k= + dependencies: + concat-stream "1.6.2" + debug "2.6.9" + mkdirp "0.5.1" + yauzl "2.4.1" + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -2217,23 +2410,23 @@ fast-deep-equal@^2.0.1: integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= fast-glob@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.3.tgz#d09d378e9ef6b0076a0fa1ba7519d9d4d9699c28" - integrity sha512-NiX+JXjnx43RzvVFwRWfPKo4U+1BrK5pJPsHQdKMlLoFHrrGktXglQhHliSihWAq+m1z6fHk3uwGHrtRbS9vLA== + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== dependencies: "@mrmlnc/readdir-enhanced" "^2.2.1" - "@nodelib/fs.stat" "^1.0.1" + "@nodelib/fs.stat" "^1.1.2" glob-parent "^3.1.0" is-glob "^4.0.0" - merge2 "^1.2.1" + 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" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.4: +fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -2243,6 +2436,35 @@ fastparse@^1.1.1: resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== +fd-slicer@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" + integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= + dependencies: + pend "~1.2.0" + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + +figures@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -2263,10 +2485,12 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" -flatten@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" - integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" for-in@^1.0.2: version "1.0.2" @@ -2304,9 +2528,23 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= + +fs-extra@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^1.2.7: @@ -2317,21 +2555,47 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" -function-bind@^1.1.0, function-bind@^1.1.1: +function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + get-port@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw= +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9" + integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== + 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" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= +getos@3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.4.tgz#29cdf240ed10a70c049add7b6f8cb08c81876faf" + integrity sha512-UORPzguEB/7UG5hqiZai8f0vQ7hzynMQyJLxStoQ8dPGAcmgsfXOPA4iE/fGtweHYkK+z4zc9V0g+CIFRf5HYw== + dependencies: + async "^3.1.0" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -2352,10 +2616,10 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@^7.0.3, glob@^7.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" - integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== +glob@^7.1.3, glob@^7.1.4: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -2364,15 +2628,22 @@ glob@^7.0.3, glob@^7.1.3: 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" + integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= + dependencies: + ini "^1.3.4" + globals@^11.1.0: - version "11.9.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.9.0.tgz#bde236808e987f290768a93d065060d78e6ab249" - integrity sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg== + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -graceful-fs@^4.1.11: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= +graceful-fs@^4.1.11, graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" + integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== grapheme-breaker@^0.3.2: version "0.3.2" @@ -2412,10 +2683,15 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= -has-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" - integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== has-value@^0.3.1: version "0.3.1" @@ -2448,7 +2724,7 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has@^1.0.0, has@^1.0.1: +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" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== @@ -2464,9 +2740,9 @@ hash-base@^3.0.0: safe-buffer "^5.0.1" hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.5" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.5.tgz#e38ab4b85dfb1e0c40fe9265c0e9b54854c23812" - integrity sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA== + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== dependencies: inherits "^2.0.3" minimalistic-assert "^1.0.1" @@ -2496,9 +2772,9 @@ hsla-regex@^1.0.0: integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= 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" - integrity sha1-ZouTd26q5V696POtRkswekljYl4= + version "1.1.2" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" + integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== html-encoding-sniffer@^1.0.2: version "1.0.2" @@ -2513,40 +2789,41 @@ html-tags@^1.0.0: integrity sha1-x43mW1Zjqll5id0rerSSANfk25g= htmlnano@^0.2.2: - version "0.2.3" - resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-0.2.3.tgz#ff654a641e8006c893bdd9ad4988ee4ab664449a" - integrity sha512-iS6T3J5gk2wInodbtMUyAU8sLYJOhuWDnIEd8lFRoHTypVGgawPHFEx2ZIK/XTErtDfwHBsrXeCwHAP8bdoSWw== - dependencies: - cssnano "^4.1.9" - normalize-html-whitespace "^0.2.0" - object-assign "^4.0.1" - posthtml "^0.11.3" - posthtml-render "^1.1.4" - svgo "^1.0.5" - terser "^3.16.1" - uncss "^0.16.2" + version "0.2.5" + resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-0.2.5.tgz#134fd9548c7cbe51c8508ce434a3f9488cff1b0b" + integrity sha512-X1iPSwXG/iF9bVs+/obt2n6F64uH0ETkA8zp7qFDmLW9/+A6ueHGeb/+qD67T21qUY22owZPMdawljN50ajkqA== + dependencies: + cssnano "^4.1.10" + normalize-html-whitespace "^1.0.0" + posthtml "^0.12.0" + posthtml-render "^1.1.5" + purgecss "^1.4.0" + svgo "^1.3.2" + terser "^4.3.9" + uncss "^0.17.2" htmlparser2@^3.9.2: - version "3.9.2" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" - integrity sha1-G9+HrMoPP55T+k/M6w9LTLsAszg= + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== dependencies: - domelementtype "^1.3.0" + domelementtype "^1.3.1" domhandler "^2.3.0" domutils "^1.5.1" entities "^1.1.1" inherits "^2.0.1" - readable-stream "^2.0.2" + readable-stream "^3.1.1" -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== dependencies: depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" http-signature@~1.2.0: version "1.2.0" @@ -2562,6 +2839,11 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -2575,20 +2857,28 @@ icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0: integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= ieee754@^1.1.4: - version "1.1.12" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" - integrity sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA== + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -2597,16 +2887,26 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + invariant@^2.2.2: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -2619,6 +2919,11 @@ is-absolute-url@^2.0.0: resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= +is-absolute-url@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -2655,10 +2960,17 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.3, is-callable@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" - integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== +is-callable@^1.1.4, is-callable@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" + integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== + +is-ci@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" is-color-stop@^1.0.0: version "1.1.0" @@ -2687,9 +2999,9 @@ is-data-descriptor@^1.0.0: kind-of "^6.0.0" is-date-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" - integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== is-descriptor@^0.1.0: version "0.1.6" @@ -2731,6 +3043,18 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -2739,19 +3063,27 @@ is-glob@^3.1.0: is-extglob "^2.1.0" is-glob@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" - integrity sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A= + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" -is-html@^1.0.0: +is-html@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-html/-/is-html-1.1.0.tgz#e04f1c18d39485111396f9a0273eab51af218464" integrity sha1-4E8cGNOUhRETlvmgJz6rUa8hhGQ= dependencies: html-tags "^1.0.0" +is-installed-globally@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" + integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA= + dependencies: + global-dirs "^0.1.0" + is-path-inside "^1.0.0" + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -2764,25 +3096,54 @@ is-obj@^1.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-observable@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e" + integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA== + dependencies: + symbol-observable "^1.1.0" + +is-path-inside@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" + integrity sha1-jvW33lBDej/cprToZe96pVy0gDY= + dependencies: + path-is-inside "^1.0.1" + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= + +is-regex@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== dependencies: - has "^1.0.1" + has "^1.0.3" is-resolvable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + is-svg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" @@ -2791,11 +3152,11 @@ is-svg@^3.0.0: html-comment-regex "^1.1.0" is-symbol@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" - integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== dependencies: - has-symbols "^1.0.0" + has-symbols "^1.0.1" is-typedarray@~1.0.0: version "1.0.0" @@ -2832,7 +3193,7 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -isobject@^2.0.0, isobject@^2.1.0: +isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= @@ -2850,19 +3211,19 @@ isstream@~0.1.2: integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= js-levenshtein@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.4.tgz#3a56e3cbf589ca0081eb22cd9ba0b1290a16d26e" - integrity sha512-PxfGzSs0ztShKrUYPIn5r0MtyAhYcCwmndozzpz8YObbPnD1jFxzlBGbRnX2mIu6Z13xN6+PTu05TQFnZFlzow== + version "1.1.6" + resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" + integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.10.0, js-yaml@^3.12.0, js-yaml@^3.9.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" - integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A== +js-yaml@^3.10.0, js-yaml@^3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -2872,36 +3233,36 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^11.3.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" - integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== +jsdom@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-14.1.0.tgz#916463b6094956b0a6c1782c94e380cd30e1981b" + integrity sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng== dependencies: abab "^2.0.0" - acorn "^5.5.3" - acorn-globals "^4.1.0" + acorn "^6.0.4" + acorn-globals "^4.3.0" array-equal "^1.0.0" - cssom ">= 0.3.2 < 0.4.0" - cssstyle "^1.0.0" - data-urls "^1.0.0" + cssom "^0.3.4" + cssstyle "^1.1.1" + data-urls "^1.1.0" domexception "^1.0.1" - escodegen "^1.9.1" + escodegen "^1.11.0" html-encoding-sniffer "^1.0.2" - left-pad "^1.3.0" - nwsapi "^2.0.7" - parse5 "4.0.0" + nwsapi "^2.1.3" + parse5 "5.1.0" pn "^1.1.0" - request "^2.87.0" + request "^2.88.0" request-promise-native "^1.0.5" - sax "^1.2.4" + saxes "^3.1.9" symbol-tree "^3.2.2" - tough-cookie "^2.3.4" + tough-cookie "^2.5.0" w3c-hr-time "^1.0.1" + w3c-xmlserializer "^1.1.2" webidl-conversions "^4.0.2" - whatwg-encoding "^1.0.3" - whatwg-mimetype "^2.1.0" - whatwg-url "^6.4.1" - ws "^5.2.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^7.0.0" + ws "^6.1.2" xml-name-validator "^3.0.0" jsesc@^2.5.1: @@ -2942,12 +3303,19 @@ json5@^1.0.1: minimist "^1.2.0" json5@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" - integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== + version "2.1.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" + integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== 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" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -2982,10 +3350,10 @@ kind-of@^6.0.0, kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== -left-pad@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" - integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== +lazy-ass@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= levn@~0.3.0: version "0.3.0" @@ -2995,6 +3363,58 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +listr-silent-renderer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" + integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= + +listr-update-renderer@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2" + integrity sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA== + dependencies: + chalk "^1.1.3" + cli-truncate "^0.2.1" + elegant-spinner "^1.0.1" + figures "^1.7.0" + indent-string "^3.0.0" + log-symbols "^1.0.2" + log-update "^2.3.0" + strip-ansi "^3.0.1" + +listr-verbose-renderer@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db" + integrity sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw== + dependencies: + chalk "^2.4.1" + cli-cursor "^2.1.0" + date-fns "^1.27.2" + figures "^2.0.0" + +listr@0.14.3: + version "0.14.3" + resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" + integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== + dependencies: + "@samverschueren/stream-to-observable" "^0.3.0" + is-observable "^1.1.0" + is-promise "^2.1.0" + is-stream "^1.1.0" + listr-silent-renderer "^1.1.1" + listr-update-renderer "^0.5.0" + listr-verbose-renderer "^0.5.0" + p-map "^2.0.0" + rxjs "^6.3.3" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + lodash.clone@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" @@ -3005,6 +3425,11 @@ lodash.memoize@^4.1.2: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -3015,16 +3440,25 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== - -lodash@^4.17.13: +lodash@4.17.15, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.4: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +log-symbols@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== + dependencies: + chalk "^2.4.2" + +log-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= + dependencies: + chalk "^1.0.0" + log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" @@ -3032,7 +3466,16 @@ log-symbols@^2.2.0: dependencies: chalk "^2.0.1" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: +log-update@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" + integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg= + dependencies: + ansi-escapes "^3.0.0" + cli-cursor "^2.0.0" + wrap-ansi "^3.0.1" + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -3051,6 +3494,11 @@ map-cache@^0.2.2: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" @@ -3067,10 +3515,10 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" -mdn-data@~1.1.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-1.1.4.tgz#50b5d4ffc4575276573c4eedb8780812a8419f01" - integrity sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA== +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== merge-source-map@1.0.4: version "1.0.4" @@ -3079,10 +3527,15 @@ merge-source-map@1.0.4: dependencies: source-map "^0.5.6" -merge2@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.2.tgz#03212e3da8d86c4d8523cebd6318193414f94e34" - integrity sha512-bgM8twH86rWni21thii6WCMQMRMmwqqdW3sGWi9IipnVAszdLXRjwDwAnyrVXo6DuP3AjRMMttZKUB48QWIFGg== +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.2.3: + version "1.3.0" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" + integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" @@ -3111,28 +3564,33 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@~1.38.0: - version "1.38.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" - integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== +mime-db@1.43.0: + version "1.43.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" + integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.22" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" - integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog== + version "2.1.26" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" + integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== dependencies: - mime-db "~1.38.0" + mime-db "1.43.0" -mime@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" - integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -3155,36 +3613,46 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@^1.1.3, minimist@^1.2.0: +minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= mixin-deep@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" - integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ== + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== dependencies: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@0.5.1, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" +moment@2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1: +ms@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + nan@^2.12.1: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" @@ -3212,10 +3680,10 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-addon-api@^1.6.0: - version "1.6.3" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.6.3.tgz#3998d4593e2dca2ea82114670a4eb003386a9fe1" - integrity sha512-FXWH6mqjWgU8ewuahp4spec8LkroFZK2NicOv6bNwZC3kcwZUI8LeZdG80UzTSLLhK4T7MsgNwlYDVRlDdfTDg== +node-addon-api@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.1.tgz#cf813cd69bb8d9100f6bdca6755fc268f54ac492" + integrity sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ== node-forge@^0.7.1: version "0.7.6" @@ -3223,9 +3691,9 @@ node-forge@^0.7.1: integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== node-libs-browser@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" - integrity sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg== + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== dependencies: assert "^1.1.1" browserify-zlib "^0.2.0" @@ -3234,10 +3702,10 @@ node-libs-browser@^2.0.0: constants-browserify "^1.0.0" crypto-browserify "^3.11.0" domain-browser "^1.1.1" - events "^1.0.0" + events "^3.0.0" https-browserify "^1.0.0" os-browserify "^0.3.0" - path-browserify "0.0.0" + path-browserify "0.0.1" process "^0.11.10" punycode "^1.2.4" querystring-es3 "^0.2.0" @@ -3248,34 +3716,20 @@ node-libs-browser@^2.0.0: timers-browserify "^2.0.4" tty-browserify "0.0.0" url "^0.11.0" - util "^0.10.3" - vm-browserify "0.0.4" - -node-releases@^1.0.0-alpha.12: - version "1.0.0-alpha.12" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.0.0-alpha.12.tgz#32e461b879ea76ac674e511d9832cf29da345268" - integrity sha512-VPB4rTPqpVyWKBHbSa4YPFme3+8WHsOSpvbp0Mfj0bWsC8TEjt4HQrLl1hsBDELlp1nB4lflSgSuGTYiuyaP7Q== - dependencies: - semver "^5.3.0" - -node-releases@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.0.3.tgz#3414ed84595096459c251699bfcb47d88324a9e4" - integrity sha512-ZaZWMsbuDcetpHmYeKWPO6e63pSXLb50M7lJgCbcM2nC/nQC3daNifmtp5a2kp7EWwYfhuvH6zLPWkrF8IiDdw== - dependencies: - semver "^5.3.0" + util "^0.11.0" + vm-browserify "^1.0.1" -node-releases@^1.1.42: - version "1.1.43" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.43.tgz#2c6ca237f88ce11d49631f11190bb01f8d0549f2" - integrity sha512-Rmfnj52WNhvr83MvuAWHEqXVoZXCcDQssSOffU4n4XOL9sPrP61mSZ88g25NqmABDvH7PiAlFCzoSCSdzA293w== +node-releases@^1.1.44: + version "1.1.45" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.45.tgz#4cf7e9175d71b1317f15ffd68ce63bce1d53e9f2" + integrity sha512-cXvGSfhITKI8qsV116u2FTzH5EWZJfgG7d4cpqwF8I8+1tWpD6AsvvGRKq2onR0DNj1jfqsjkXZsm14JMS7Cyg== dependencies: semver "^6.3.0" -normalize-html-whitespace@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/normalize-html-whitespace/-/normalize-html-whitespace-0.2.0.tgz#101722f6423551c75cdb8f9d104ff850daf1e10e" - integrity sha1-EBci9kI1Ucdc24+dEE/4UNrx4Q4= +normalize-html-whitespace@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/normalize-html-whitespace/-/normalize-html-whitespace-1.0.0.tgz#5e3c8e192f1b06c3b9eee4b7e7f28854c7601e34" + integrity sha512-9ui7CGtOOlehQu0t/OhhlmDyc71mKVlv+4vF+me4iZLPrNtRL2xoquEdfZxasC/bdQi/Hr3iTrpyRKIG+ocabA== normalize-path@^2.1.1: version "2.1.1" @@ -3294,24 +3748,43 @@ normalize-url@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== -nth-check@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" - integrity sha1-mSms32KPwsQQmN6rgqxYDPFJquQ= +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.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" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== dependencies: boolbase "~1.0.0" -nwsapi@^2.0.7: - version "2.1.3" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.1.3.tgz#25f3a5cec26c654f7376df6659cdf84b99df9558" - integrity sha512-RowAaJGEgYXEZfQ7tvvdtAQUKPyTR6T6wNu0fwlNsGQYr/h3yQc6oI8WnVZh3Y/Sylwc+dtAlvPqfFZjhTyk3A== +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +nwsapi@^2.1.3: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.0.1, object-assign@^4.1.1: +object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -3325,21 +3798,21 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-inspect@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" + integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== + object-inspect@~1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.4.1.tgz#37ffb10e71adaf3748d05f713b4c9452f402cbc4" integrity sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw== -object-keys@^1.0.11: +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object-keys@^1.0.12, object-keys@^1.0.6: - version "1.0.12" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" - integrity sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag== - object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" @@ -3358,12 +3831,12 @@ object.assign@^4.1.0: object-keys "^1.0.11" object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" - integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= + version "2.1.0" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" + integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" object.pick@^1.3.0: version "1.3.0" @@ -3372,15 +3845,15 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.0.4.tgz#e524da09b4f66ff05df457546ec72ac99f13069a" - integrity sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo= +object.values@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== dependencies: - define-properties "^1.1.2" - es-abstract "^1.6.1" - function-bind "^1.1.0" - has "^1.0.1" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" on-finished@~2.3.0: version "2.3.0" @@ -3389,13 +3862,18 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -once@^1.3.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= + onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -3403,24 +3881,31 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +onetime@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== + dependencies: + mimic-fn "^2.1.0" + opn@^5.1.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.4.0.tgz#cb545e7aab78562beb11aa3bfabc7042e1761035" - integrity sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw== + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== dependencies: is-wsl "^1.1.0" optionator@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" - integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" - fast-levenshtein "~2.0.4" + fast-levenshtein "~2.0.6" levn "~0.3.0" prelude-ls "~1.1.2" type-check "~0.3.2" - wordwrap "~1.0.0" + word-wrap "~1.2.3" ora@^2.1.0: version "2.1.0" @@ -3439,15 +3924,54 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= +ospath@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" + integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-finally@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" + integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== + +p-limit@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" + integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== + dependencies: + p-try "^2.0.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + pako@^0.2.5: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= pako@~1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" - integrity sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg== + version "1.0.10" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" + integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== parcel-bundler@1.12.4: version "1.12.4" @@ -3515,15 +4039,16 @@ parcel-bundler@1.12.4: ws "^5.1.1" parse-asn1@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" - integrity sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw== + version "5.1.5" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" + integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ== dependencies: asn1.js "^4.0.0" browserify-aes "^1.0.0" create-hash "^1.1.0" evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" parse-json@^4.0.0: version "4.0.0" @@ -3533,46 +4058,68 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse5@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" - integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== +parse5@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" + integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== -parseurl@~1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" - integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -path-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" - integrity sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo= +path-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^2.0.1: +path-is-inside@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-parse@^1.0.5: +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= + dependencies: + through "~2.3" + pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -3584,6 +4131,11 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -3594,6 +4146,11 @@ physical-cpu-count@^2.0.0: resolved "https://registry.yarnpkg.com/physical-cpu-count/-/physical-cpu-count-2.0.0.tgz#18de2f97e4bf7a9551ad7511942b5496f7aba660" integrity sha1-GN4vl+S/epVRrXURlCtUlverpmA= +pify@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" @@ -3604,16 +4161,6 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= -postcss-calc@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-6.0.2.tgz#4d9a43e27dbbf27d095fecb021ac6896e2318337" - integrity sha512-fiznXjEN5T42Qm7qqMCVJXS3roaj9r4xsSi+meaBVe7CJBl8t/QLOXu02Z2E6oWAMWIvCuF6JrvzFekmVEbOKA== - dependencies: - css-unit-converter "^1.1.1" - postcss "^7.0.2" - postcss-selector-parser "^2.2.2" - reduce-css-calc "^2.0.0" - postcss-calc@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.1.tgz#36d77bab023b0ecbb9789d84dcb23c4941145436" @@ -3624,17 +4171,6 @@ postcss-calc@^7.0.1: postcss-selector-parser "^5.0.0-rc.4" postcss-value-parser "^3.3.1" -postcss-colormin@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.2.tgz#93cd1fa11280008696887db1a528048b18e7ed99" - integrity sha512-1QJc2coIehnVFsz0otges8kQLsryi4lo19WD+U5xCWvXd0uw/Z+KKYnbiNDCnO9GP+PvErPHCG0jNvWTngk9Rw== - dependencies: - browserslist "^4.0.0" - color "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - postcss-colormin@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" @@ -3654,13 +4190,6 @@ postcss-convert-values@^4.0.1: postcss "^7.0.0" postcss-value-parser "^3.0.0" -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" - integrity sha512-Ay+rZu1Sz6g8IdzRjUgG2NafSNpp2MSMOQUb+9kkzzzP+kh07fP0yNbhtFejURnyVXSX3FYy2nVNW1QTnNjgBQ== - dependencies: - postcss "^7.0.0" - postcss-discard-comments@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" @@ -3699,28 +4228,6 @@ postcss-merge-longhand@^4.0.11: postcss-value-parser "^3.0.0" stylehacks "^4.0.0" -postcss-merge-longhand@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.6.tgz#2b938fa3529c3d1657e53dc7ff0fd604dbc85ff1" - integrity sha512-JavnI+V4IHWsaUAfOoKeMEiJQGXTraEy1nHM0ILlE6NIQPEZrJDAnPh3lNGZ5HAk2mSSrwp66JoGhvjp6SqShA== - dependencies: - css-color-names "0.0.4" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - stylehacks "^4.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" - integrity sha512-UiuXwCCJtQy9tAIxsnurfF0mrNHKc4NnNx6NxqmzNNjXpQwLSukUxELHTRF0Rg1pAmcoKLih8PwvZbiordchag== - 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-merge-rules@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" @@ -3741,16 +4248,6 @@ postcss-minify-font-values@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.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" - integrity sha512-pySEW3E6Ly5mHm18rekbWiAjVi/Wj8KKt2vwSfVFAWdW6wOIekgqxKxLU7vJfb107o3FDNPkaYFCxGAJBFyogA== - 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-gradients@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" @@ -3761,18 +4258,6 @@ postcss-minify-gradients@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.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" - integrity sha512-h4W0FEMEzBLxpxIVelRtMheskOKKp52ND6rJv+nBS33G1twu2tCyurYj/YtgU76+UDCvWeNs0hs8HFAWE2OUFg== - 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-params@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" @@ -3785,16 +4270,6 @@ postcss-minify-params@^4.0.2: postcss-value-parser "^3.0.0" uniqs "^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" - integrity sha512-8+plQkomve3G+CodLCgbhAKrb5lekAnLYuL1d7Nz+/7RANpBEVdgBkPNwljfSKvZ9xkkZTZITd04KP+zeJTJqg== - dependencies: - alphanum-sort "^1.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - postcss-minify-selectors@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" @@ -3843,15 +4318,6 @@ postcss-normalize-charset@^4.0.1: 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" - integrity sha512-R5mC4vaDdvsrku96yXP7zak+O3Mm9Y8IslUobk7IMP+u/g+lXvcN4jngmHY5zeJnrQvE13dfAg5ViU05ZFDwdg== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - postcss-normalize-display-values@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" @@ -3861,16 +4327,6 @@ postcss-normalize-display-values@^4.0.2: 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" - integrity sha512-GNoOaLRBM0gvH+ZRb2vKCIujzz4aclli64MBwDuYGU2EY53LwiP7MxOZGE46UGtotrSnmarPPZ69l2S/uxdaWA== - dependencies: - cssnano-util-get-arguments "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - postcss-normalize-positions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" @@ -3881,16 +4337,6 @@ postcss-normalize-positions@^4.0.2: 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" - integrity sha512-fFHPGIjBUyUiswY2rd9rsFcC0t3oRta4wxE1h3lpwfQZwFeFjXFSiDtdJ7APCmHQOnUZnqYBADNRPKPwFAONgA== - 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-repeat-style@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" @@ -3901,15 +4347,6 @@ postcss-normalize-repeat-style@^4.0.2: 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" - integrity sha512-IJoexFTkAvAq5UZVxWXAGE0yLoNN/012v7TQh5nDo6imZJl2Fwgbhy3J2qnIoaDBrtUP0H7JrXlX1jjn2YcvCQ== - dependencies: - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - postcss-normalize-string@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" @@ -3919,15 +4356,6 @@ postcss-normalize-string@^4.0.2: 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" - integrity sha512-1nOtk7ze36+63ONWD8RCaRDYsnzorrj+Q6fxkQV+mlY5+471Qx9kspqv0O/qQNMeApg8KNrRf496zHwJ3tBZ7w== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - postcss-normalize-timing-functions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" @@ -3956,14 +4384,6 @@ postcss-normalize-url@^4.0.1: 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" - integrity sha512-U8MBODMB2L+nStzOk6VvWWjZgi5kQNShCyjRhMT3s+W9Jw93yIjOnrEkKYD3Ul7ChWbEcjDWmXq0qOL9MIAnAw== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - postcss-normalize-whitespace@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" @@ -3972,15 +4392,6 @@ postcss-normalize-whitespace@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" -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" - integrity sha512-PeJiLgJWPzkVF8JuKSBcylaU+hDJ/TX3zqAMIjlghgn1JBi6QwQaDZoDIlqWRcCAI8SxKrt3FCPSRmOgKRB97Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - postcss-ordered-values@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" @@ -3990,16 +4401,6 @@ postcss-ordered-values@^4.1.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" -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" - integrity sha512-epUiC39NonKUKG+P3eAOKKZtm5OtAtQJL7Ye0CBN1f+UQTHzqotudp+hki7zxXm7tT0ZAKDMBj1uihpPjP25ug== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-reduce-initial@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" @@ -4010,16 +4411,6 @@ postcss-reduce-initial@^4.0.3: has "^1.0.0" postcss "^7.0.0" -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" - integrity sha512-sZVr3QlGs0pjh6JAIe6DzWvBaqYw05V1t3d9Tp+VnFRT5j+rsqoWsysh/iSD7YNsULjq9IAylCznIwVd5oU/zA== - dependencies: - cssnano-util-get-match "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - postcss-reduce-transforms@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" @@ -4030,21 +4421,21 @@ postcss-reduce-transforms@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" -postcss-selector-parser@3.1.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" - integrity sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU= +postcss-selector-parser@6.0.2, postcss-selector-parser@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" + integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== dependencies: - dot-prop "^4.1.1" + cssesc "^3.0.0" indexes-of "^1.0.1" uniq "^1.0.1" -postcss-selector-parser@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90" - integrity sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A= +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" + integrity sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU= dependencies: - flatten "^1.0.2" + dot-prop "^4.1.1" indexes-of "^1.0.1" uniq "^1.0.1" @@ -4057,16 +4448,6 @@ postcss-selector-parser@^5.0.0-rc.4: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-svgo@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.1.tgz#5628cdb38f015de6b588ce6d0bf0724b492b581d" - integrity sha512-YD5uIk5NDRySy0hcI+ZJHwqemv2WiqqzDgtvgMzO8EGSkK5aONyX8HMVFRFJSdO8wUWTuisUFn/d7yRRbBr5Qw== - dependencies: - is-svg "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - svgo "^1.0.0" - postcss-svgo@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" @@ -4086,12 +4467,7 @@ postcss-unique-selectors@^4.0.1: postcss "^7.0.0" uniqs "^2.0.0" -postcss-value-parser@^3.0.0, 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" - integrity sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU= - -postcss-value-parser@^3.3.1: +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" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== @@ -4105,7 +4481,7 @@ postcss@6.0.1: source-map "^0.5.6" supports-color "^3.2.3" -postcss@^6.0.1, postcss@^6.0.14: +postcss@^6.0.1: version "6.0.23" resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== @@ -4114,69 +4490,62 @@ 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.2: - version "7.0.5" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.5.tgz#70e6443e36a6d520b0fd4e7593fcca3635ee9f55" - integrity sha512-HBNpviAUFCKvEh7NZhw1e8MBPivRszIiUnhrJ+sBFVSYSqubrzwX3KG51mYgcRHX8j/cAgZJedONZcm5jTBdgQ== - dependencies: - chalk "^2.4.1" - source-map "^0.6.1" - supports-color "^5.5.0" - -postcss@^7.0.11, postcss@^7.0.5: - version "7.0.14" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.14.tgz#4527ed6b1ca0d82c53ce5ec1a2041c2346bbd6e5" - integrity sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg== +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.11, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.5: + version "7.0.26" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.26.tgz#5ed615cfcab35ba9bbb82414a4fa88ea10429587" + integrity sha512-IY4oRjpXWYshuTDFxMVkJDtWIk2LhsTlu8bZnbEJA4+bYT16Lvpo8Qv6EvDumhYRgzjZl489pmsY3qVgJQ08nA== dependencies: chalk "^2.4.2" source-map "^0.6.1" supports-color "^6.1.0" -posthtml-parser@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.3.3.tgz#3fe986fca9f00c0f109d731ba590b192f26e776d" - integrity sha512-H/Z/yXGwl49A7hYQLV1iQ3h87NE0aZ/PMZhFwhw3lKeCAN+Ti4idrHvVvh4/GX10I7u77aQw+QB4vV5/Lzvv5A== +posthtml-parser@^0.4.0, posthtml-parser@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.4.2.tgz#a132bbdf0cd4bc199d34f322f5c1599385d7c6c1" + integrity sha512-BUIorsYJTvS9UhXxPTzupIztOMVNPa/HtAm9KHni9z6qEfiJ1bpOBL5DfUOL9XAc3XkLIEzBzpph+Zbm4AdRAg== dependencies: htmlparser2 "^3.9.2" - isobject "^2.1.0" - object-assign "^4.1.1" -posthtml-parser@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.4.1.tgz#95b78fef766fbbe0a6f861b6e95582bc3d1ff933" - integrity sha512-h7vXIQ21Ikz2w5wPClPakNP6mJeJCK6BT0GpqnQrNNABdR7/TchNlFyryL1Bz6Ww53YWCKkr6tdZuHlxY1AVdQ== - dependencies: - htmlparser2 "^3.9.2" - object-assign "^4.1.1" +posthtml-render@^1.1.3, posthtml-render@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-1.1.5.tgz#387934e85438a3de77085fbc7d264efb00bd0e0f" + integrity sha512-yvt54j0zCBHQVEFAuR+yHld8CZrCa/E1Z/OcFNCV1IEWTLVxT8O7nYnM4IIw1CD4r8kaRd3lc42+0lgCKgm87w== -posthtml-render@^1.1.0, posthtml-render@^1.1.3, posthtml-render@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-1.1.4.tgz#95dac09892f4f183fad5ac823f08f42c0256551e" - integrity sha512-jL6eFIzoN3xUEvbo33OAkSDE2VIKU4JQ1wENOows1DpfnrdapR/K3Q1/fB43Mq7wQlcSgRm23nFrvoioufM7eA== +posthtml@^0.11.2: + version "0.11.6" + resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.11.6.tgz#e349d51af7929d0683b9d8c3abd8166beecc90a8" + integrity sha512-C2hrAPzmRdpuL3iH0TDdQ6XCc9M7Dcc3zEW5BLerY65G4tWWszwv6nG/ksi6ul5i2mx22ubdljgktXCtNkydkw== + dependencies: + posthtml-parser "^0.4.1" + posthtml-render "^1.1.5" -posthtml@^0.11.2, posthtml@^0.11.3: - version "0.11.3" - resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.11.3.tgz#17ea2921b0555b7455f33c977bd16d8b8cb74f27" - integrity sha512-quMHnDckt2DQ9lRi6bYLnuyBDnVzK+McHa8+ar4kTdYbWEo/92hREOu3h70ZirudOOp/my2b3r0m5YtxY52yrA== +posthtml@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.12.0.tgz#6e2a2fcd774eaed1a419a95c5cc3a92b676a40a6" + integrity sha512-aNUEP/SfKUXAt+ghG51LC5MmafChBZeslVe/SSdfKIgLGUVRE68mrMF4V8XbH07ZifM91tCSuxY3eHIFLlecQw== dependencies: - object-assign "^4.1.1" - posthtml-parser "^0.3.3" - posthtml-render "^1.1.0" + posthtml-parser "^0.4.1" + posthtml-render "^1.1.5" prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +pretty-bytes@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2" + integrity sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg== + private@^0.1.6: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== process@^0.11.10: version "0.11.10" @@ -4184,17 +4553,25 @@ process@^0.11.10: integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= prop-types@^15.6.2: - version "15.6.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" - integrity sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ== + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== dependencies: - loose-envify "^1.3.1" + loose-envify "^1.4.0" object-assign "^4.1.1" + react-is "^16.8.1" + +ps-tree@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" + integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== + dependencies: + event-stream "=3.3.4" psl@^1.1.24, psl@^1.1.28: - version "1.1.31" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" - integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== + version "1.7.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" + integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== public-encrypt@^4.0.0: version "4.0.3" @@ -4208,6 +4585,14 @@ public-encrypt@^4.0.0: randombytes "^2.0.1" safe-buffer "^5.1.2" +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -4223,6 +4608,16 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +purgecss@^1.4.0: + version "1.4.2" + resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-1.4.2.tgz#67ab50cb4f5c163fcefde56002467c974e577f41" + integrity sha512-hkOreFTgiyMHMmC2BxzdIw5DuC6kxAbP/gGOGd3MEsF3+5m69rIvUEPaxrnoUtfODTFKe9hcXjGwC6jcjoyhOw== + dependencies: + glob "^7.1.3" + postcss "^7.0.14" + postcss-selector-parser "^6.0.0" + yargs "^14.0.0" + q@^1.1.2: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -4252,10 +4647,15 @@ quote-stream@^1.0.1, quote-stream@~1.0.2: minimist "^1.1.3" through2 "^2.0.0" +ramda@0.26.1: + version "0.26.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" + integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" - integrity sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A== + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" @@ -4267,10 +4667,10 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -range-parser@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== re-classnames@4.1.0: version "4.1.0" @@ -4283,29 +4683,33 @@ re-debouncer@2.1.0: integrity sha512-Iy9BecWF9X4V6zXfL3ROZpKGkCnpzYdjG/j6NIBxQn7gEwO2DzED5BsbfRUiPeqr7C0tAD0HPHF6tYIcQFEnyA== react-dom@>=16.8.1: - version "16.8.6" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f" - integrity sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA== + version "16.12.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.12.0.tgz#0da4b714b8d13c2038c9396b54a92baea633fe11" + integrity sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.13.6" + scheduler "^0.18.0" + +react-is@^16.8.1: + version "16.12.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" + integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== react@>=16.8.1: - version "16.8.6" - resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" - integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw== + version "16.12.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83" + integrity sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.13.6" -readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.3: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== +readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.3, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -4315,6 +4719,15 @@ readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.1.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" + integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" @@ -4332,14 +4745,6 @@ reason-react@0.7.0: react ">=16.8.1" react-dom ">=16.8.1" -reduce-css-calc@^2.0.0: - version "2.1.5" - resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.5.tgz#f283712f0c9708ef952d328f4b16112d57b03714" - integrity sha512-AybiBU03FKbjYzyvJvwkJZY6NLN+80Ufc2EqEs+41yQH+8wqBEslD6eGiS0oIeq5TNLA5PrhBeYHXWdn8gtW7A== - dependencies: - css-unit-converter "^1.1.1" - postcss-value-parser "^3.3.0" - regenerate-unicode-properties@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" @@ -4404,9 +4809,9 @@ regjsgen@^0.2.0: integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= regjsgen@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" - integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== + version "0.5.1" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" + integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== regjsparser@^0.1.4: version "0.1.5" @@ -4416,9 +4821,9 @@ regjsparser@^0.1.4: jsesc "~0.5.0" regjsparser@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c" - integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ== + version "0.6.2" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.2.tgz#fd62c753991467d9d1ffe0a9f67f27a529024b96" + integrity sha512-E9ghzUtoLwDekPT0DYCp+c4h+bvuUpe6rRHCTYn6eGoqj1LgKXxT6I0Il4WbjhQkOghzi/V+y03bPKvbllL93Q== dependencies: jsesc "~0.5.0" @@ -4437,23 +4842,30 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -request-promise-core@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346" - integrity sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== +request-progress@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4= + dependencies: + throttleit "^1.0.0" + +request-promise-core@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" + integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== dependencies: - lodash "^4.17.11" + lodash "^4.17.15" -request-promise-native@^1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.7.tgz#a49868a624bdea5069f1251d0a836e0d89aa2c59" - integrity sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w== +request-promise-native@^1.0.5, request-promise-native@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" + integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== dependencies: - request-promise-core "1.1.2" + request-promise-core "1.1.3" stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.72.0, request@^2.87.0: +request@2.88.0, request@^2.88.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== @@ -4479,17 +4891,40 @@ request@^2.72.0, request@^2.87.0: tunnel-agent "^0.6.0" uuid "^3.3.2" +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= resolve@^1.1.5, resolve@^1.3.2, resolve@^1.4.0: - version "1.8.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" - integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== + version "1.14.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" + integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== dependencies: - path-parse "^1.0.5" + path-parse "^1.0.6" + +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" restore-cursor@^2.0.0: version "2.0.0" @@ -4514,10 +4949,10 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -rimraf@^2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== +rimraf@^2.6.2, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" @@ -4529,7 +4964,19 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +rxjs@^6.3.3, rxjs@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" + integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== + dependencies: + tslib "^1.9.0" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -4546,15 +4993,22 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sax@^1.2.4, sax@~1.2.4: +sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -scheduler@^0.13.6: - version "0.13.6" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889" - integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ== +saxes@^3.1.9: + version "3.1.11" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" + integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== + dependencies: + xmlchars "^2.1.1" + +scheduler@^0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.18.0.tgz#5901ad6659bc1d8f3fdaf36eb7a67b0d6746b1c4" + integrity sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -4564,20 +5018,20 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@^5.3.0, semver@^5.4.1, semver@^5.5.0: - version "5.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" - integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== +semver@^5.4.1, semver@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -send@0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" - integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== dependencies: debug "2.6.9" depd "~1.1.2" @@ -4586,12 +5040,12 @@ send@0.16.2: escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" + range-parser "~1.2.1" + statuses "~1.5.0" serialize-to-js@^3.0.0: version "3.0.2" @@ -4599,29 +5053,24 @@ serialize-to-js@^3.0.0: integrity sha512-o5FqeMyxGx1wkp8p14q9QqGXh1JjXtIDYTr15N/B4ThM5ULqlpXdtOO84m950jFGvBkeRD1utW+WyNKvao2ybQ== serve-static@^1.12.4: - version "1.13.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" - integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" - parseurl "~1.3.2" - send "0.16.2" - -set-value@^0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" - integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE= - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.1" - to-object-path "^0.3.0" + parseurl "~1.3.3" + send "0.17.1" -set-value@^2.0.0: +set-blocking@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" - integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg== + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" @@ -4633,10 +5082,10 @@ setimmediate@^1.0.4: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" @@ -4658,12 +5107,24 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= -signal-exit@^3.0.2: +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= @@ -4675,6 +5136,11 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -4706,28 +5172,20 @@ snapdragon@^0.8.1: use "^3.1.0" 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" - integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== dependencies: - atob "^2.1.1" + atob "^2.1.2" decode-uri-component "^0.2.0" resolve-url "^0.2.1" source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@~0.5.10: - version "0.5.12" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" - integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-support@~0.5.6: - version "0.5.9" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f" - integrity sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA== +source-map-support@~0.5.10, source-map-support@~0.5.12: + version "0.5.16" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" + integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -4742,7 +5200,7 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6: +source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -4754,6 +5212,13 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= + dependencies: + through "2" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -4774,17 +5239,30 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -stable@~0.1.6: +stable@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +start-server-and-test@1.10.10: + version "1.10.10" + resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.10.10.tgz#ad3dd8d389f9487214b617e884f85cca74244549" + integrity sha512-LCnngrqi4Ex7FDJwpzfByXK8SCAcITYJB5yj/K3k2onaBHcH6RMFf6q3Kz8iM7CLuJ3DZlzU76W6Ixwpt+n/sQ== + dependencies: + bluebird "3.7.2" + check-more-types "2.24.0" + debug "4.1.1" + execa "3.4.0" + lazy-ass "1.6.0" + ps-tree "1.2.0" + wait-on "4.0.0" + static-eval@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.0.tgz#0e821f8926847def7b4b50cda5d55c04a9b13864" - integrity sha512-6flshd3F1Gwm+Ksxq463LtFd1liC77N/PX1FVVc3OzL3hAmo2fwHFbuArkcfi7s9rTNsLEhcRmXGFZhlgy40uw== + version "2.0.3" + resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.3.tgz#cb62fc79946bd4d5f623a45ad428233adace4d72" + integrity sha512-zsxDGucfAh8T339sSKgpFbvg15Fms2IVaJGC+jqp0bVsxhcpM+iMeAI8weNo8dmf4OblgifTBUoyk1vGVtYw2w== dependencies: - escodegen "^1.8.1" + escodegen "^1.11.1" static-extend@^0.1.1: version "0.1.2" @@ -4814,29 +5292,31 @@ static-module@^2.2.0: static-eval "^2.0.0" through2 "~2.0.3" -"statuses@>= 1.4.0 < 2": +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -statuses@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" - integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== - stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= stream-browserify@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" - integrity sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds= + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== dependencies: inherits "~2.0.1" readable-stream "^2.0.2" +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= + dependencies: + duplexer "~0.1.1" + stream-http@^2.7.2: version "2.8.3" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" @@ -4848,14 +5328,63 @@ stream-http@^2.7.2: to-arraybuffer "^1.0.0" xtend "^4.0.0" -string_decoder@^1.0.0, string_decoder@~1.1.1: +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string.prototype.trimleft@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" + integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string.prototype.trimright@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" + integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" -strip-ansi@^3.0.0: +strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= @@ -4869,15 +5398,39 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + stylehacks@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.1.tgz#3186595d047ab0df813d213e51c8b94e0b9010f2" - integrity sha512-TK5zEPeD9NyC1uPIdjikzsgWxdQQN/ry1X3d1iOz1UkYDCmcr928gWD1KHgyC27F50UnE0xCTrBOO1l6KR8M4w== + version "4.0.3" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" + integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== dependencies: browserslist "^4.0.0" postcss "^7.0.0" postcss-selector-parser "^3.0.0" +supports-color@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -4890,7 +5443,7 @@ supports-color@^3.2.3: dependencies: has-flag "^1.0.0" -supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0: +supports-color@^5.3.0, supports-color@^5.4.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -4904,32 +5457,36 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -svgo@^1.0.0, svgo@^1.0.5: - version "1.1.1" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.1.1.tgz#12384b03335bcecd85cfa5f4e3375fed671cb985" - integrity sha512-GBkJbnTuFpM4jFbiERHDWhZc/S/kpHToqmZag3aEBjPYK44JAN2QBjvrGIxLOoCyMZjuFQIfTO2eJd8uwLY/9g== +svgo@^1.0.0, svgo@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" + integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== dependencies: - coa "~2.0.1" - colors "~1.1.2" + chalk "^2.4.1" + coa "^2.0.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" + css-select-base-adapter "^0.1.1" + css-tree "1.0.0-alpha.37" + csso "^4.0.2" + js-yaml "^3.13.1" mkdirp "~0.5.1" - object.values "^1.0.4" + object.values "^1.1.0" sax "~1.2.4" - stable "~0.1.6" + stable "^0.1.8" unquote "~1.1.1" util.promisify "~1.0.0" +symbol-observable@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + symbol-tree@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" - integrity sha1-rifbOPZgp64uHDt9G8KQgZuFGeY= + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -terser@^3.16.1: +terser@^3.7.3: version "3.17.0" resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2" integrity sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ== @@ -4938,27 +5495,37 @@ terser@^3.16.1: source-map "~0.6.1" source-map-support "~0.5.10" -terser@^3.7.3: - version "3.9.3" - resolved "https://registry.yarnpkg.com/terser/-/terser-3.9.3.tgz#14d13207f58b7a25ba7dd11def76c9e73cca5036" - integrity sha512-7CAUcdTRzfxvMUqhSDe95MQ/qVEV3JqiSB8mMGQZSe1CL7AKB1iMpF7Mj6DatC9YfG/4xxE25Egp1kxVNORdGQ== +terser@^4.3.9: + version "4.6.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.2.tgz#cb1cf055e7f70caa5863f00ba3e67dc3c97b5150" + integrity sha512-6FUjJdY2i3WZAtYBtnV06OOcOfzl+4hSKYE9wgac8rkLRBToPDDrBB2AcHwQD/OKDxbnvhVy2YgOPWO2SsKWqg== dependencies: - commander "~2.17.1" + commander "^2.20.0" source-map "~0.6.1" - source-map-support "~0.5.6" + source-map-support "~0.5.12" + +throttleit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= through2@^2.0.0, through2@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" - integrity sha1-AARWmzfHx0ujnEPzzteNGtlBQL4= + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== dependencies: - readable-stream "^2.1.5" + readable-stream "~2.3.6" xtend "~4.0.1" +through@2, through@~2.3, through@~2.3.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + timers-browserify@^2.0.4: - version "2.0.10" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" - integrity sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg== + version "2.0.11" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" + integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ== dependencies: setimmediate "^1.0.4" @@ -4968,9 +5535,16 @@ timsort@^0.3.0: integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= tiny-inflate@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.2.tgz#93d9decffc8805bd57eae4310f0b745e9b6fb3a7" - integrity sha1-k9nez/yIBb1X6uQxDwt0Xptvs6c= + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" + integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== + +tmp@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" + integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== + dependencies: + rimraf "^2.6.3" to-arraybuffer@^1.0.0: version "1.0.1" @@ -5012,7 +5586,12 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -tough-cookie@^2.3.3, tough-cookie@^2.3.4: +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@^2.3.3, tough-cookie@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== @@ -5035,6 +5614,11 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" +tslib@^1.9.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" + integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -5064,20 +5648,20 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -uncss@^0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/uncss/-/uncss-0.16.2.tgz#3b2269c59012da7c66cbe98fbedddeef94f0649c" - integrity sha1-OyJpxZAS2nxmy+mPvt3e75TwZJw= - dependencies: - commander "^2.9.0" - glob "^7.0.3" - is-absolute-url "^2.0.0" - is-html "^1.0.0" - jsdom "^11.3.0" - lodash "^4.13.1" - postcss "^6.0.14" - postcss-selector-parser "3.1.1" - request "^2.72.0" +uncss@^0.17.2: + version "0.17.2" + resolved "https://registry.yarnpkg.com/uncss/-/uncss-0.17.2.tgz#fac1c2429be72108e8a47437c647d58cf9ea66f1" + integrity sha512-hu2HquwDItuGDem4YsJROdAD8SknmWtM24zwhQax6J1se8tPjV1cnwPKhtjodzBaUhaL8Zb3hlGdZ2WAUpbAOg== + dependencies: + commander "^2.20.0" + glob "^7.1.4" + is-absolute-url "^3.0.1" + is-html "^1.1.0" + jsdom "^14.1.0" + lodash "^4.17.15" + postcss "^7.0.17" + postcss-selector-parser "6.0.2" + request "^2.88.0" unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" @@ -5098,9 +5682,9 @@ unicode-match-property-value-ecmascript@^1.1.0: integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== unicode-property-aliases-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz#5a533f31b4317ea76f17d807fa0d116546111dd0" - integrity sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg== + version "1.0.5" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" + integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== unicode-trie@^0.3.1: version "0.3.1" @@ -5111,14 +5695,14 @@ unicode-trie@^0.3.1: tiny-inflate "^1.0.0" union-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" - integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ= + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== dependencies: arr-union "^3.1.0" get-value "^2.0.6" is-extendable "^0.1.1" - set-value "^0.4.3" + set-value "^2.0.1" uniq@^1.0.1: version "1.0.1" @@ -5130,6 +5714,11 @@ uniqs@^2.0.0: resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + unquote@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" @@ -5143,6 +5732,11 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +untildify@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + upath@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" @@ -5160,7 +5754,7 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url@^0.11.0: +url@0.11.0, url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= @@ -5173,7 +5767,7 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -5193,27 +5787,27 @@ util@0.10.3: dependencies: inherits "2.0.1" -util@^0.10.3: - version "0.10.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" - integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== dependencies: inherits "2.0.3" uuid@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + version "3.3.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" + integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== v8-compile-cache@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c" - integrity sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw== + version "2.1.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" + integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== vendors@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.2.tgz#7fcb5eef9f5623b156bcea89ec37d63676f21801" - integrity sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ== + version "1.0.3" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.3.tgz#a6467781abd366217c050f8202e7e50cc9eef8c0" + integrity sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw== verror@1.10.0: version "1.10.0" @@ -5229,12 +5823,10 @@ vlq@^0.2.2: resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow== -vm-browserify@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" - integrity sha1-XX6kW7755Kb/ZflUOOCofDV9WnM= - dependencies: - indexof "0.0.1" +vm-browserify@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== w3c-hr-time@^1.0.1: version "1.0.1" @@ -5243,6 +5835,27 @@ w3c-hr-time@^1.0.1: dependencies: browser-process-hrtime "^0.1.2" +w3c-xmlserializer@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" + integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== + dependencies: + domexception "^1.0.1" + webidl-conversions "^4.0.2" + xml-name-validator "^3.0.0" + +wait-on@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-4.0.0.tgz#4d7e4485ca759968897fd3b0cc50720c0b4ca959" + integrity sha512-QrW3J8LzS5ADPfD9Rx5S6KJck66xkqyiFKQs9jmUTkIhiEOmkzU7WRZc+MjsnmkrgjitS2xQ4bb13hnlQnKBUQ== + dependencies: + "@hapi/joi" "^16.1.8" + lodash "^4.17.15" + minimist "^1.2.0" + request "^2.88.0" + request-promise-native "^1.0.8" + rxjs "^6.5.4" + wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -5255,36 +5868,32 @@ webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" -whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0: +whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^6.4.1: - version "6.5.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" - integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - whatwg-url@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd" - integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ== + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== dependencies: lodash.sortby "^4.7.0" tr46 "^1.0.1" webidl-conversions "^4.0.2" +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -5292,29 +5901,110 @@ which@^1.2.9: dependencies: isexe "^2.0.0" -wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" + integrity sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo= + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -ws@^5.1.1, ws@^5.2.0: +ws@^5.1.1: version "5.2.2" resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== dependencies: async-limiter "~1.0.0" +ws@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + dependencies: + async-limiter "~1.0.0" + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xmlchars@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + xtend@^4.0.0, xtend@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yargs-parser@^15.0.0: + version "15.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.0.tgz#cdd7a97490ec836195f59f3f4dbe5ea9e8f75f08" + integrity sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^14.0.0: + version "14.2.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.2.tgz#2769564379009ff8597cdd38fba09da9b493c4b5" + integrity sha512-/4ld+4VV5RnrynMhPZJ/ZpOCGSCeghMykZ3BhdFBDa9Wy/RH6uEGNWDJog+aUlq+9OM1CFTgtYRW5Is1Po9NOA== + dependencies: + cliui "^5.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^15.0.0" + +yauzl@2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yauzl@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" + integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU= + dependencies: + fd-slicer "~1.0.1"