diff --git a/HISTORY.md b/HISTORY.md
index e0fc4855..41503c53 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,5 +1,8 @@
# History
+## 4.0.0-beta.4
+* Removed targets and changed update handlers API. See: [#68](https://github.com/MinimaHQ/re-formality/issues/68) & [#78](https://github.com/MinimaHQ/re-formality/pull/78).
+
## 4.0.0-beta.3
* Added `ReactDom` & `ReactNative` targets and changed update handlers API. See: [#68](https://github.com/MinimaHQ/re-formality/issues/68) & [#72](https://github.com/MinimaHQ/re-formality/pull/72).
diff --git a/docs/01-Installation.md b/docs/01-Installation.md
index 3bbd88b2..8d4dfc35 100644
--- a/docs/01-Installation.md
+++ b/docs/01-Installation.md
@@ -19,32 +19,6 @@ Under the hood, `re-formality` implemented as PPX. So you need to add it to both
],
```
-## Targets
-Library supports 2 targets:
-- `ReactDom`
-- `ReactNative`
-
-By default, it's set to `ReactDom`. But you can configure it in quite flexible ways:
-1. If you want to apply specific target to all modules in the build, set environment variable `FORMALITY_TARGET` to chosen target:
-
-```shell
-FORMALITY_TARGET=ReactNative bsb -clean-world -make-world
-```
-
-2. If you want to set specific target on per module basis, do this in your form module:
-
-```reason
-module MyForm = [%form
- {target: ReactNative};
-
- type input = ...;
- type output = ...;
- ...
-];
-```
-
----
-
Before proceeding with actual code, we will elaborate on some core concepts that this library implements.
---
diff --git a/docs/04-BasicUsage.md b/docs/04-BasicUsage.md
index bfc9f944..43c711e1 100644
--- a/docs/04-BasicUsage.md
+++ b/docs/04-BasicUsage.md
@@ -174,24 +174,36 @@ Next thing to render is a text input for `email` field:
form.blurEmail()}
onChange={
- form.updateEmail((~target, input) => {
- ...input,
- email: target##value,
- })
+ event =>
+ form.updateEmail(
+ (input, value) => {...input, email: value},
+ event->ReactEvent.Form.target##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: ReactEvent.Focus.t => unit`: must be triggered from `onBlur` handler of an input field
-2. `form.updateEmail: ((~target: Js.t({..}), input) => input, ReactEvent.Form.t) => unit`: must be triggered from `onChange` handler of an input field. It takes a function as an argument which takes the event target and the current form `input`, must return updated `input` record. Event target, as defined in `reason-react` bindings, is an open object (`Js.t({..})`), which means it is not type-safe but allows to access any target property you need. Most of the time you need either `target##value` or `target##checked`.
+1. `form.blurEmail: unit => unit`: must be triggered from `onBlur` handler of an input field
+2. `form.updateEmail: ((input, 'inputValue) => input, 'inputValue) => unit`: must be triggered from `onChange` handler of an input field. It takes 2 arguments:
+ - a function which takes 2 arguments—the current form `input` and updated input value of the current field—and returns updated `input` record
+ - an updated input value of the current field
-For ReactNative users, type signatures would be a bit different since those don't include events related stuff:
+The second argument—updated input value—that gets passed to the `form.updateEmail` is exactly the same value as a second argument of the callback. Why it's done this way? Why not just use this value within the callback? It is designed this way to ensure that synthetic DOM event won't be captured by the callback.
-1. `form.blurEmail: unit => unit`
-2. `form.updateEmail: (input => input) => unit`
+```reason
+// Bad
+onChange={event => {
+ form.updateEmail(input => {
+ ...input,
+ email: event->ReactEvent.Form.target##value,
+ });
+}}
+```
+
+As you might already know, [React's `SyntheticEvent` is pooled](https://reactjs.org/docs/events.html#event-pooling). If you would capture the event in the callback (as shown above), since the callback gets triggered asynchronously, by the time it gets called, the event is already null'ed by React and it will result in a runtime error. To avoid this, we ensure that the value is extracted from event outside of the callback.
### Messages
To display feedback in UI, we can use `form.emailResult` value. It's exactly what email validator returns but wrapped in `option` type:
@@ -325,12 +337,13 @@ let make = () => {
form.blurEmail()}
onChange={
- form.updateEmail((~target, input) => {
- ...input,
- email: target##value,
- })
+ event =>
+ form.updateEmail(
+ (input, value) => {...input, email: value},
+ event->ReactEvent.Form.target##value,
+ )
}
/>
{switch (form.emailResult) {
@@ -342,12 +355,13 @@ let make = () => {
form.blurPassword()}
onChange={
- form.updatePassword((~target, input) => {
- ...input,
- password: target##value,
- })
+ event =>
+ form.updatePassword(
+ (input, value) => {...input, password: value},
+ event->ReactEvent.Form.target##value,
+ )
}
/>
{switch (form.passwordResult) {
diff --git a/docs/06-Collections.md b/docs/06-Collections.md
index cb2772b5..a542b39a 100644
--- a/docs/06-Collections.md
+++ b/docs/06-Collections.md
@@ -86,8 +86,8 @@ Getting the input and results, as well as handling addition, removal and field u
- `form.addAuthor({name: ""})`: adds `author` entry to collection
- `form.removeAuthor(~at: index)`: removes `author` entry from collection
-- `form.blurAuthorName(~at: index, ReactEvent.Focus.t)`: triggers blur in `author.name` field at index
-- `form.updateAuthorName(~at: index, (~target: Js.t({..}), input) => input, ReactEvent.Form.t)`: updates `author.name` field at index
+- `form.blurAuthorName(~at: index)`: triggers blur in `author.name` field at index
+- `form.updateAuthorName(~at: index, (input, 'inputValue) => input, 'inputValue)`: 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
@@ -104,22 +104,25 @@ let form = MyForm.useForm(...);
form.blurAuthorName(~at=index)}
onChange={
- form.updateAuthorName(~at=index, (~target, input) =>
- {
- ...input,
- authors:
- input.authors
- ->Array.mapWithIndex((idx, author) =>
- if (idx != index) {
- author;
- } else {
- {name: target##value};
- }
- ),
- }
- )
+ event =>
+ form.updateAuthorName(
+ ~at=index,
+ (input, value) => {
+ ...input,
+ authors:
+ input.authors
+ ->Array.mapWithIndex((idx, author) =>
+ if (idx != index) {
+ author;
+ } else {
+ {name: value};
+ }
+ ),
+ },
+ event->ReactEvent.Form.target##value,
+ )
}
/>
option(bool),
// General field
- update[Field]: ((~target: Js.t({..}), input) => input, ReactEvent.Form.t) => unit,
- blur[Field]: ReactEvent.Focus.t => unit,
+ update[Field]: ((input, 'inputValue) => input, 'inputValue) => unit,
+ blur[Field]: unit => unit,
[field]Result: option(result('outputValue, message)),
// Async field
- update[Field]: ((~target: Js.t({..}), input) => input, ReactEvent.Form.t) => unit,
- blur[Field]: ReactEvent.Focus.t => unit,
+ update[Field]: ((input, 'inputValue) => input, 'inputValue) => unit,
+ blur[Field]: unit => unit,
[field]Result: option(Formality.Async.exposedFieldStatus('outputValue, message)),
// Field of collection
- update[CollectionEntry][Field]: (~at: index, (~target: Js.t({..}), input) => input, ReactEvent.Form.t) => unit,
- blur[CollectionEntry][Field]: (~at: index, ReactEvent.Focus.t) => unit,
+ update[CollectionEntry][Field]: (~at: index, (input, 'inputValue) => input, 'inputValue) => unit,
+ blur[CollectionEntry][Field]: (~at: index) => unit,
[collectionEntry][Field]Result: (~at: index) => option(result('outputValue, message)),
// Collection
@@ -292,42 +292,18 @@ type interface = {
#### Update handlers
Used to update form input for a specific field.
-**Target: `ReactDom`**
-
-```reason
-// Field
-update[Field]: ((~target: Js.t({..}), input) => input, ReactEvent.Form.t) => unit
-
-// Field of collection
-update[CollectionEntry][Field]: (~at: index, (~target: Js.t({..}), input) => input, ReactEvent.Form.t) => unit,
-```
-
-**Target: `ReactNative`**
-
```reason
// Field
-update[Field]: (input => input) => unit
+update[Field]: ((input, 'inputValue) => input, 'inputValue) => unit
// Field of collection
-update[CollectionEntry][Field]: (~at: index, input => input) => unit,
+update[CollectionEntry][Field]: (~at: index, (input, 'inputValue) => input, 'inputValue) => unit,
```
#### Blur handlers
Used to notify hook on blur event for a specific field.
-**Target: `ReactDom`**
-
-```reason
-// Field
-blur[Field]: ReactEvent.Focus.t => unit
-
-// Field of collection
-blur[CollectionEntry][Field]: (~at: index, ReactEvent.Focus.t) => unit,
-```
-
-**Target: `ReactNative`**
-
```reason
// Field
blur[Field]: unit => unit
diff --git a/examples/package.json b/examples/package.json
index 30d6a4ea..2e89a675 100644
--- a/examples/package.json
+++ b/examples/package.json
@@ -4,6 +4,7 @@
"private": true,
"scripts": {
"start": "parcel src/index.html",
+ "prestart": "yarn run clean && yarn run bs:build",
"build": "parcel build src/index.html",
"prebuild": "yarn run clean && yarn run bs:build",
"clean": "yarn run dist:clean && yarn run bs:clean",
diff --git a/examples/src/BlogPostForm.re b/examples/src/BlogPostForm.re
index 3183f3e4..1d220f9c 100644
--- a/examples/src/BlogPostForm.re
+++ b/examples/src/BlogPostForm.re
@@ -78,10 +78,11 @@ let make = () => {
type_="text"
value={form.input.title}
disabled={form.submitting}
- onBlur={form.blurTitle}
- onChange={
- form.updateTitle((~target, input) =>
- {...input, title: target##value}
+ onBlur={_ => form.blurTitle()}
+ onChange={event =>
+ form.updateTitle(
+ (input, value) => {...input, title: value},
+ event->ReactEvent.Form.target##value,
)
}
/>
@@ -137,21 +138,24 @@ let make = () => {
type_="text"
value={author.name}
disabled={form.submitting}
- onBlur={form.blurAuthorName(~at=index)}
- onChange={
- form.updateAuthorName(~at=index, (~target, input) =>
- {
- ...input,
- authors:
- input.authors
- ->Array.mapWithIndex((idx, author) =>
- if (idx != index) {
- author;
- } else {
- {name: target##value};
- }
- ),
- }
+ onBlur={_ => form.blurAuthorName(~at=index)}
+ onChange={event =>
+ form.updateAuthorName(
+ ~at=index,
+ (input, value) =>
+ {
+ ...input,
+ authors:
+ input.authors
+ ->Array.mapWithIndex((idx, author) =>
+ if (idx != index) {
+ author;
+ } else {
+ {name: value};
+ }
+ ),
+ },
+ event->ReactEvent.Form.target##value,
)
}
/>
diff --git a/examples/src/LoginForm.re b/examples/src/LoginForm.re
index d30f9489..149375c4 100644
--- a/examples/src/LoginForm.re
+++ b/examples/src/LoginForm.re
@@ -66,10 +66,11 @@ let make = () => {
type_="text"
value={form.input.email}
disabled={form.submitting}
- onBlur={form.blurEmail}
- onChange={
- form.updateEmail((~target, input) =>
- {...input, email: target##value}
+ onBlur={_ => form.blurEmail()}
+ onChange={event =>
+ form.updateEmail(
+ (input, value) => {...input, email: value},
+ event->ReactEvent.Form.target##value,
)
}
/>
@@ -104,10 +105,11 @@ let make = () => {
type_="text"
value={form.input.password}
disabled={form.submitting}
- onBlur={form.blurPassword}
- onChange={
- form.updatePassword((~target, input) =>
- {...input, password: target##value}
+ onBlur={_ => form.blurPassword()}
+ onChange={event =>
+ form.updatePassword(
+ (input, value) => {...input, password: value},
+ event->ReactEvent.Form.target##value,
)
}
/>
@@ -140,10 +142,11 @@ let make = () => {
checked={form.input.rememberMe}
disabled={form.submitting}
className="push-lg"
- onBlur={form.blurRememberMe}
- onChange={
- form.updateRememberMe((~target, input) =>
- {...input, rememberMe: target##checked}
+ onBlur={_ => form.blurRememberMe()}
+ onChange={event =>
+ form.updateRememberMe(
+ (input, value) => {...input, rememberMe: value},
+ event->ReactEvent.Form.target##checked,
)
}
/>
diff --git a/examples/src/SignupForm.re b/examples/src/SignupForm.re
index 4f1a7818..8b08fff4 100644
--- a/examples/src/SignupForm.re
+++ b/examples/src/SignupForm.re
@@ -91,10 +91,11 @@ let make = () => {
type_="text"
value={form.input.email}
disabled={form.submitting}
- onBlur={form.blurEmail}
- onChange={
- form.updateEmail((~target, input) =>
- {...input, email: target##value}
+ onBlur={_ => form.blurEmail()}
+ onChange={event =>
+ form.updateEmail(
+ (input, value) => {...input, email: value},
+ event->ReactEvent.Form.target##value,
)
}
/>
@@ -139,10 +140,11 @@ let make = () => {
type_="text"
value={form.input.password}
disabled={form.submitting}
- onBlur={form.blurPassword}
- onChange={
- form.updatePassword((~target, input) =>
- {...input, password: target##value}
+ onBlur={_ => form.blurPassword()}
+ onChange={event =>
+ form.updatePassword(
+ (input, value) => {...input, password: value},
+ event->ReactEvent.Form.target##value,
)
}
/>
@@ -177,10 +179,11 @@ let make = () => {
type_="text"
value={form.input.passwordConfirmation}
disabled={form.submitting}
- onBlur={form.blurPasswordConfirmation}
- onChange={
- form.updatePasswordConfirmation((~target, input) =>
- {...input, passwordConfirmation: target##value}
+ onBlur={_ => form.blurPasswordConfirmation()}
+ onChange={event =>
+ form.updatePasswordConfirmation(
+ (input, value) => {...input, passwordConfirmation: value},
+ event->ReactEvent.Form.target##value,
)
}
/>
diff --git a/lib/ppx/Form.re b/lib/ppx/Form.re
index 63271401..3ec3407e 100644
--- a/lib/ppx/Form.re
+++ b/lib/ppx/Form.re
@@ -16,8 +16,6 @@ let ext =
switch (structure |> Metadata.make) {
| Ok({
scheme,
- target,
- structure,
async,
output_type,
message_type,
@@ -29,11 +27,10 @@ let ext =
// 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
- // 2. Insert target mark (for testing and debugging)
- // 4. Inject types and values that either optional and weren't provided
+ // 3. Inject types and values that either optional and weren't provided
// or just generated by ppx
- // 5. Update validators record (see Form_ValidatorsRecord for details)
- // 6. Append neccessary functions including useForm hook
+ // 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.
@@ -42,7 +39,6 @@ let ext =
let head = [
Form_Attributes.ast(~loc),
Form_OpenFormality.ast(~loc),
- Form_Target.ast(~target, ~loc),
];
let types = {
@@ -53,7 +49,7 @@ let ext =
Form_StateType.ast(~loc),
Form_ActionType.ast(~scheme, ~loc),
Form_ValidatorsType.ast(~scheme, ~loc),
- Form_InterfaceType.ast(~scheme, ~target, ~async, ~loc),
+ Form_InterfaceType.ast(~scheme, ~async, ~loc),
]);
switch (submission_error_type) {
| None => types := [SubmissionErrorType.default(~loc), ...types^]
@@ -89,7 +85,7 @@ let ext =
async
? Form_ValidateFormFn.Async.ast(~scheme, ~loc)
: Form_ValidateFormFn.Sync.ast(~scheme, ~loc),
- Form_UseFormFn.ast(~scheme, ~target, ~async, ~loc),
+ Form_UseFormFn.ast(~scheme, ~async, ~loc),
];
let structure: structure =
@@ -139,11 +135,6 @@ let ext =
Mod.mk(Pmod_structure(List.append(head, structure)));
- | Error(TargetParseError(InvalidTarget(loc))) =>
- Location.raise_errorf(
- ~loc,
- "Invalid target. Supported targets are `ReactDom` and `ReactNative`",
- )
| Error(InputTypeParseError(NotFound)) =>
Location.raise_errorf(~loc, "`input` type not found")
| Error(InputTypeParseError(NotRecord(loc))) =>
diff --git a/lib/ppx/Form_InterfaceType.re b/lib/ppx/Form_InterfaceType.re
index ba908cc8..06e43504 100644
--- a/lib/ppx/Form_InterfaceType.re
+++ b/lib/ppx/Form_InterfaceType.re
@@ -6,7 +6,7 @@ open Printer;
open Ppxlib;
open Ast_helper;
-let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => {
+let ast = (~scheme: Scheme.t, ~async: bool, ~loc) => {
let f = (x, t) => t |> Type.field(x |> str(~loc));
let base = [
@@ -28,8 +28,6 @@ let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => {
f("reset", [%type: unit => unit]),
];
- let dom_event_target = [%type: Js.t({..} as 'eventTarget)];
-
let update_fns =
scheme
|> List.fold_left(
@@ -38,16 +36,13 @@ let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => {
| Field(field) => [
f(
FieldPrinter.update_fn(~field=field.name),
- switch (target) {
- | ReactDom => [%type:
- (
- (~target: [%t dom_event_target], input) => input,
- ReactEvent.Form.t
- ) =>
- unit
- ]
- | ReactNative => [%type: (input => input) => unit]
- },
+ [%type:
+ (
+ (input, [%t field.input_type |> ItemType.unpack]) => input,
+ [%t field.input_type |> ItemType.unpack]
+ ) =>
+ unit
+ ],
),
...acc,
]
@@ -61,19 +56,18 @@ let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => {
~collection,
~field=field.name,
),
- switch (target) {
- | ReactDom => [%type:
+ [%type:
+ (
+ ~at: index,
(
- ~at: index,
- (~target: [%t dom_event_target], input) => input,
- ReactEvent.Form.t
+ input,
+ [%t field.input_type |> ItemType.unpack]
) =>
- unit
- ]
- | ReactNative => [%type:
- (~at: index, input => input) => unit
- ]
- },
+ input,
+ [%t field.input_type |> ItemType.unpack]
+ ) =>
+ unit
+ ],
),
...acc,
],
@@ -91,10 +85,7 @@ let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => {
| Field(field) => [
f(
FieldPrinter.blur_fn(~field=field.name),
- switch (target) {
- | ReactDom => [%type: ReactEvent.Focus.t => unit]
- | ReactNative => [%type: unit => unit]
- },
+ [%type: unit => unit],
),
...acc,
]
@@ -108,12 +99,7 @@ let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => {
~collection,
~field=field.name,
),
- switch (target) {
- | ReactDom => [%type:
- (~at: index, ReactEvent.Focus.t) => unit
- ]
- | ReactNative => [%type: (~at: index) => unit]
- },
+ [%type: (~at: index) => unit],
),
...acc,
],
@@ -242,11 +228,6 @@ let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => {
"interface"
|> str(~loc)
|> Type.mk(
- ~params=?
- switch (target) {
- | ReactDom => Some([([%type: 'eventTarget], Invariant)])
- | ReactNative => None
- },
~kind=
Ptype_record(
base
diff --git a/lib/ppx/Form_Target.re b/lib/ppx/Form_Target.re
deleted file mode 100644
index eb120c10..00000000
--- a/lib/ppx/Form_Target.re
+++ /dev/null
@@ -1,12 +0,0 @@
-open Meta;
-open Ast;
-open AstHelpers;
-
-open Ppxlib;
-open Ast_helper;
-
-let ast = (~target: Target.t, ~loc) =>
- switch (target) {
- | ReactDom => [%stri let _target = "ReactDom"]
- | ReactNative => [%stri let _target = "ReactNative"]
- };
diff --git a/lib/ppx/Form_UseFormFn.re b/lib/ppx/Form_UseFormFn.re
index cddcff3f..c6419317 100644
--- a/lib/ppx/Form_UseFormFn.re
+++ b/lib/ppx/Form_UseFormFn.re
@@ -5,7 +5,7 @@ open AstHelpers;
open Ppxlib;
open Ast_helper;
-let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => [%stri
+let ast = (~scheme: Scheme.t, ~async: bool, ~loc) => [%stri
let useForm =
(
~initialInput: input,
@@ -38,7 +38,7 @@ let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => [%stri
%e
{
- Form_UseFormFn_Interface.ast(~scheme, ~target, ~async, ~loc);
+ Form_UseFormFn_Interface.ast(~scheme, ~async, ~loc);
};
}
];
diff --git a/lib/ppx/Form_UseFormFn_Interface.re b/lib/ppx/Form_UseFormFn_Interface.re
index cc40a8af..79b8d7b3 100644
--- a/lib/ppx/Form_UseFormFn_Interface.re
+++ b/lib/ppx/Form_UseFormFn_Interface.re
@@ -50,7 +50,7 @@ let dirty_collection_guard = (~loc, {collection, fields}: Scheme.collection) =>
})
];
-let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => {
+let ast = (~scheme: Scheme.t, ~async: bool, ~loc) => {
let collections = scheme |> Scheme.collections;
let base = [
@@ -188,41 +188,22 @@ let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => {
| Field(field) => [
(
FieldPrinter.update_fn(~field=field.name),
- switch (target) {
- | ReactDom =>
- %expr
+ [%expr
(
- (nextInputFn, event) => {
- let target = event->ReactEvent.Form.target;
+ (nextInputFn, nextValue) => {
[%e
Exp.construct(
Lident(
FieldPrinter.update_action(~field=field.name),
)
|> lid(~loc),
- Some([%expr nextInputFn(~target)]),
+ Some([%expr nextInputFn(_, nextValue)]),
)
]
->dispatch;
}
)
- | ReactNative =>
- %expr
- (
- nextInputFn => {
- [%e
- Exp.construct(
- Lident(
- FieldPrinter.update_action(~field=field.name),
- )
- |> lid(~loc),
- Some([%expr nextInputFn]),
- )
- ]
- ->dispatch;
- }
- )
- },
+ ],
),
...acc,
]
@@ -236,49 +217,25 @@ let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => {
~collection,
~field=field.name,
),
- switch (target) {
- | ReactDom =>
- %expr
- (
- (~at as index, nextInputFn, event) => {
- let target = event->ReactEvent.Form.target;
- [%e
- Exp.construct(
- Lident(
- FieldOfCollectionPrinter.update_action(
- ~collection,
- ~field=field.name,
- ),
- )
- |> lid(~loc),
- Some(
- [%expr (nextInputFn(~target), index)],
+ [%expr
+ (~at as index, nextInputFn, nextValue) => {
+ [%e
+ Exp.construct(
+ Lident(
+ FieldOfCollectionPrinter.update_action(
+ ~collection,
+ ~field=field.name,
),
)
- ]
- ->dispatch;
- }
- )
- | ReactNative =>
- %expr
- (
- (~at as index, nextInputFn) => {
- [%e
- Exp.construct(
- Lident(
- FieldOfCollectionPrinter.update_action(
- ~collection,
- ~field=field.name,
- ),
- )
- |> lid(~loc),
- Some([%expr (nextInputFn, index)]),
- )
- ]
- ->dispatch;
- }
- )
- },
+ |> lid(~loc),
+ Some(
+ [%expr (nextInputFn(_, nextValue), index)],
+ ),
+ )
+ ]
+ ->dispatch;
+ }
+ ],
),
...acc,
],
@@ -296,22 +253,21 @@ let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => {
| Field(field) => [
(
FieldPrinter.blur_fn(~field=field.name),
- {
- let action =
- Exp.construct(
- Lident(FieldPrinter.blur_action(~field=field.name))
- |> lid(~loc),
- None,
- );
- switch (target) {
- | ReactDom =>
- %expr
- ((_event: ReactEvent.Focus.t) => [%e action]->dispatch)
- | ReactNative =>
- %expr
- (() => [%e action]->dispatch)
- };
- },
+ [%expr
+ (
+ () =>
+ [%e
+ Exp.construct(
+ Lident(
+ FieldPrinter.blur_action(~field=field.name),
+ )
+ |> lid(~loc),
+ None,
+ )
+ ]
+ ->dispatch
+ )
+ ],
),
...acc,
]
@@ -325,30 +281,22 @@ let ast = (~scheme: Scheme.t, ~target: Target.t, ~async: bool, ~loc) => {
~collection,
~field=field.name,
),
- {
- let action =
- Exp.construct(
- Lident(
- FieldOfCollectionPrinter.blur_action(
- ~collection,
- ~field=field.name,
- ),
+ [%expr
+ (~at as index) =>
+ [%e
+ Exp.construct(
+ Lident(
+ FieldOfCollectionPrinter.blur_action(
+ ~collection,
+ ~field=field.name,
+ ),
+ )
+ |> lid(~loc),
+ Some([%expr index]),
)
- |> lid(~loc),
- Some([%expr index]),
- );
- switch (target) {
- | ReactDom =>
- %expr
- (
- (~at as index, _event: ReactEvent.Focus.t) =>
- [%e action]->dispatch
- )
- | ReactNative =>
- %expr
- ((~at as index) => [%e action]->dispatch)
- };
- },
+ ]
+ ->dispatch
+ ],
),
...acc,
],
diff --git a/lib/ppx/Meta.re b/lib/ppx/Meta.re
index c7fe93be..05aa80c9 100644
--- a/lib/ppx/Meta.re
+++ b/lib/ppx/Meta.re
@@ -2,65 +2,6 @@ open Ast;
open Ppxlib;
-module Target = {
- type t =
- | ReactDom
- | ReactNative;
-
- type error =
- | InvalidTarget(location);
-
- exception EmptyFormalityTarget;
- exception InvalidFormalityTarget(string);
-
- let default = ReactDom;
-
- let fromEnv = () =>
- switch (Sys.getenv_opt("FORMALITY_TARGET")) {
- | Some("ReactNative") => ReactNative
- | Some("ReactDom") => ReactDom
- | None => default
- | Some("") => raise(EmptyFormalityTarget)
- | Some(target) => raise(InvalidFormalityTarget(target))
- };
-
- let fromModule = (structure: structure) =>
- switch (structure) {
- | [item, ...items] =>
- switch (item) {
- | {
- pstr_desc:
- Pstr_eval(
- {
- pexp_desc:
- Pexp_record(
- [
- (
- {txt: Lident("target")},
- {
- pexp_desc:
- Pexp_construct({txt: Lident(target)}, None),
- pexp_loc,
- },
- ),
- ],
- None,
- ),
- },
- [],
- ),
- } =>
- switch (target) {
- | "ReactDom" => Ok(Some((ReactDom, items)))
- | "ReactNative" => Ok(Some((ReactNative, items)))
- | _ => Error(InvalidTarget(pexp_loc))
- }
- | _ => Ok(None)
- }
- | [] => Ok(None)
- };
-};
-
module ItemType = {
module T: {type t;} = {
type t = core_type;
@@ -1655,8 +1596,6 @@ module ValidatorsRecordParser = {
module Metadata = {
type t = {
scheme: Scheme.t,
- structure,
- target: Target.t,
async: bool, // meh, it should be variant: Sync(_) | Async(_)
output_type: OutputTypeParser.ok,
validators_record: ValidatorsRecord.t,
@@ -1666,7 +1605,6 @@ module Metadata = {
};
type error =
- | TargetParseError(Target.error)
| InputTypeParseError(InputTypeParser.error)
| OutputTypeParseError(OutputTypeParser.error)
| ValidatorsRecordParseError(ValidatorsRecordParser.error)
@@ -1695,328 +1633,356 @@ module Metadata = {
let submission_error_type: ref(option(unit)) = ref(None);
let debounce_interval_value: ref(option(unit)) = ref(None);
- switch (structure |> Target.fromModule) {
- | Error(error) => Error(TargetParseError(error))
- | Ok(res) =>
- let (target, structure) =
- switch (res) {
- | Some(x) => x
- | None => (Target.fromEnv(), structure)
- };
-
- 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}, []),
- }),
- } =>
+ 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(OutputTypeParser.BadTypeAlias({alias, loc}))
- | {ptype_name: {txt: "output"}, ptype_loc} =>
+ Error(InputNotAvailable(ptype_loc))
+ | Some(Ok({entries})) =>
output_parsing_result :=
- Error(OutputTypeParser.NotRecord(ptype_loc))
-
- // Message type
- | {ptype_name: {txt: "message"}, ptype_loc} =>
- message_type := Some()
+ 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 => ()
+ };
+ }
+ | _ => (),
+ );
- // Submission error type
- | {ptype_name: {txt: "submissionError"}, ptype_loc} =>
- submission_error_type := Some()
+ 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,
+ }),
+ )
+ };
- // 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 => ()
- };
- }
- | _ => (),
- );
+ 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)
+ };
- 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),
- )),
- ),
+ | (
+ 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 =>
+ ),
+ )
+ }
+ | None =>
+ if (ItemType.eq(input_field_data.typ, output_field_data.typ)) {
switch (
validators_record.fields
- |> ValidatorsRecordParser.optional(field)
+ |> 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()))
- }
- }
- | 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) =>
+ };
+ } else {
switch (
validators_record.fields
|> ValidatorsRecordParser.required(input_field)
@@ -2035,440 +2001,390 @@ module Metadata = {
`BadRequiredValidator((
input_field,
reason,
- `IncludedInDeps(in_deps_of_field),
+ `DifferentIO((
+ input_field_data.typ,
+ output_field_data.typ,
+ )),
)),
),
),
)
- }
- | 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(
+ }
+ | 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,
(
- 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,
- )
+ 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
- }
- ),
- )
+ | (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
- }
- ),
+ | (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,
- }),
- ),
+ | 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_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,
(
- 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,
- [
+ 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_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_field_data,
+ ~output_field_data,
+ ~input_entries=validated_input_entries,
+ ~validators_record,
);
- };
- },
- 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,
+ (
+ 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
)
- ) {
- | Ok(res) => Ok(res)
- | Error(_) => Error()
- },
- }),
- ...scheme,
- ]),
+ }
+ ),
+ );
+ };
+ },
+ 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,
- }),
- ),
- )
- };
+ };
+ },
+ 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,
- target,
- structure,
- 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)
};
- }
- };
+
+ 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/test/Test.re b/lib/test/Test.re
index 09b587ee..b6f3bc3b 100644
--- a/lib/test/Test.re
+++ b/lib/test/Test.re
@@ -41,18 +41,13 @@ let () =
"Ok__CollectionWithNoCollectionValidatorAndTwoFieldsOfCollectionWithSyncValidator",
"Ok__Message",
"Ok__SubmissionError",
- "Ok__Target",
"Ok__Include",
]
|> List.map(ok),
),
(
"errors",
- [
- "Error__InputNotFound",
- "Error__InputNotRecord",
- "Error__InvalidTarget",
- ]
+ ["Error__InputNotFound", "Error__InputNotRecord"]
|> List.map(error),
),
],
diff --git a/lib/test/cases/Error__InvalidTarget.re b/lib/test/cases/Error__InvalidTarget.re
deleted file mode 100644
index 01a2d2f6..00000000
--- a/lib/test/cases/Error__InvalidTarget.re
+++ /dev/null
@@ -1,5 +0,0 @@
-module Form = [%form
- {target: ReactBative};
- type input = {name: string};
- let validators = {name: None}
-];
diff --git a/lib/test/cases/Error__InvalidTarget.snapshot b/lib/test/cases/Error__InvalidTarget.snapshot
deleted file mode 100644
index ca37a2a1..00000000
--- a/lib/test/cases/Error__InvalidTarget.snapshot
+++ /dev/null
@@ -1,11 +0,0 @@
-
- We've found a bug for you!
- ./test/cases/Error__InvalidTarget.re 2:12-22
-
- 1 │ module Form = [%form
- 2 │ {target: ReactBative};
- 3 │ type input = {name: string};
- 4 │ let validators = {name: None}
-
- Invalid target. Supported targets are `ReactDom` and `ReactNative`
-
diff --git a/lib/test/cases/Ok__Target.re b/lib/test/cases/Ok__Target.re
deleted file mode 100644
index 77666f10..00000000
--- a/lib/test/cases/Ok__Target.re
+++ /dev/null
@@ -1,10 +0,0 @@
-module Form = [%form
- {target: ReactNative};
- type input = {name: string};
- let validators = {
- name: {
- strategy: OnSubmit,
- validate: ({name}) => Ok(name),
- },
- }
-];
diff --git a/package.json b/package.json
index a57b3e35..f82650a1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "re-formality",
- "version": "4.0.0-beta.3",
+ "version": "4.0.0-beta.4",
"description": "Form validation tool for reason-react",
"author": "Alex Fedoseev ",
"license": "MIT",
diff --git a/specs/app/Placeholder.re b/specs/app/Placeholder.re
index 2b8b1f14..9cceb0d0 100644
--- a/specs/app/Placeholder.re
+++ b/specs/app/Placeholder.re
@@ -27,8 +27,13 @@ let make = () => {
id="field--name--input"
value={form.input.name}
disabled={form.submitting}
- onBlur={form.blurName}
- onChange={form.updateName((~target, _input) => {name: target##value})}
+ onBlur={_ => form.blurName()}
+ onChange={event =>
+ form.updateName(
+ (_input, value) => {name: value},
+ event->ReactEvent.Form.target##value,
+ )
+ }
/>
{switch (form.nameResult) {
| Some(Error(message)) =>
diff --git a/specs/cypress.json b/specs/cypress.json
index aa6fdd9e..34c3844c 100644
--- a/specs/cypress.json
+++ b/specs/cypress.json
@@ -1,5 +1,6 @@
{
"baseUrl": "http://localhost:8080",
+ "video": false,
"integrationFolder": "tests",
"screenshotsFolder": "screenshots",
"fixturesFolder": "fixtures",
diff --git a/specs/package.json b/specs/package.json
index 04e1a839..3395c39a 100644
--- a/specs/package.json
+++ b/specs/package.json
@@ -15,7 +15,7 @@
"dependencies": {
"bs-platform": "7.2.2",
"bsb-js": "1.1.7",
- "cypress": "4.1.0",
+ "cypress": "4.4.1",
"parcel-bundler": "1.12.4",
"re-formality": "*",
"reason-react": "0.7.0",
diff --git a/yarn.lock b/yarn.lock
index 95ae31f3..c4509d63 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -706,6 +706,32 @@
date-fns "^1.27.2"
figures "^1.7.0"
+"@cypress/request@2.88.5":
+ version "2.88.5"
+ resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.5.tgz#8d7ecd17b53a849cfd5ab06d5abe7d84976375d7"
+ integrity sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA==
+ dependencies:
+ aws-sign2 "~0.7.0"
+ aws4 "^1.8.0"
+ caseless "~0.12.0"
+ combined-stream "~1.0.6"
+ extend "~3.0.2"
+ forever-agent "~0.6.1"
+ form-data "~2.3.2"
+ har-validator "~5.1.3"
+ http-signature "~1.2.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.19"
+ oauth-sign "~0.9.0"
+ performance-now "^2.1.0"
+ qs "~6.5.2"
+ safe-buffer "^5.1.2"
+ tough-cookie "~2.5.0"
+ tunnel-agent "^0.6.0"
+ uuid "^3.3.2"
+
"@cypress/xvfb@1.2.4":
version "1.2.4"
resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a"
@@ -818,12 +844,94 @@
dependencies:
any-observable "^0.3.0"
+"@types/blob-util@1.3.3":
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/@types/blob-util/-/blob-util-1.3.3.tgz#adba644ae34f88e1dd9a5864c66ad651caaf628a"
+ integrity sha512-4ahcL/QDnpjWA2Qs16ZMQif7HjGP2cw3AGjHabybjw7Vm1EKu+cfQN1D78BaZbS1WJNa1opSMF5HNMztx7lR0w==
+
+"@types/bluebird@3.5.29":
+ version "3.5.29"
+ resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.29.tgz#7cd933c902c4fc83046517a1bef973886d00bdb6"
+ integrity sha512-kmVtnxTuUuhCET669irqQmPAez4KFnFVKvpleVRyfC3g+SHD1hIkFZcWLim9BVcwUBLO59o8VZE4yGCmTif8Yw==
+
+"@types/chai-jquery@1.1.40":
+ version "1.1.40"
+ resolved "https://registry.yarnpkg.com/@types/chai-jquery/-/chai-jquery-1.1.40.tgz#445bedcbbb2ae4e3027f46fa2c1733c43481ffa1"
+ integrity sha512-mCNEZ3GKP7T7kftKeIs7QmfZZQM7hslGSpYzKbOlR2a2HCFf9ph4nlMRA9UnuOETeOQYJVhJQK7MwGqNZVyUtQ==
+ dependencies:
+ "@types/chai" "*"
+ "@types/jquery" "*"
+
+"@types/chai@*":
+ version "4.2.11"
+ resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.11.tgz#d3614d6c5f500142358e6ed24e1bf16657536c50"
+ integrity sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==
+
+"@types/chai@4.2.7":
+ version "4.2.7"
+ resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.7.tgz#1c8c25cbf6e59ffa7d6b9652c78e547d9a41692d"
+ integrity sha512-luq8meHGYwvky0O7u0eQZdA7B4Wd9owUCqvbw2m3XCrCU8mplYOujMBbvyS547AxJkC+pGnd0Cm15eNxEUNU8g==
+
+"@types/jquery@*":
+ version "3.3.35"
+ resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.35.tgz#ab2cbf97e7a04b4dc0faee22b93c633fa540891c"
+ integrity sha512-pnIELWhHXJ7RgoFylhiTxD+96QlKBJfEx8JCLj963/dh7zBOKFkZ6rlNqbaCcn2JZrsAxCI8WhgRXznBx2iDsA==
+ dependencies:
+ "@types/sizzle" "*"
+
+"@types/jquery@3.3.31":
+ version "3.3.31"
+ resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.31.tgz#27c706e4bf488474e1cb54a71d8303f37c93451b"
+ integrity sha512-Lz4BAJihoFw5nRzKvg4nawXPzutkv7wmfQ5121avptaSIXlDNJCUuxZxX/G+9EVidZGuO0UBlk+YjKbwRKJigg==
+ dependencies:
+ "@types/sizzle" "*"
+
+"@types/lodash@4.14.149":
+ version "4.14.149"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.149.tgz#1342d63d948c6062838fbf961012f74d4e638440"
+ integrity sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==
+
+"@types/minimatch@3.0.3":
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
+ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
+
+"@types/mocha@5.2.7":
+ version "5.2.7"
+ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea"
+ integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==
+
"@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":
+"@types/sinon-chai@3.2.3":
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-3.2.3.tgz#afe392303dda95cc8069685d1e537ff434fa506e"
+ integrity sha512-TOUFS6vqS0PVL1I8NGVSNcFaNJtFoyZPXZ5zur+qlhDfOmQECZZM4H4kKgca6O8L+QceX/ymODZASfUfn+y4yQ==
+ dependencies:
+ "@types/chai" "*"
+ "@types/sinon" "*"
+
+"@types/sinon@*":
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.0.tgz#5b70a360f55645dd64f205defd2a31b749a59799"
+ integrity sha512-v2TkYHkts4VXshMkcmot/H+ERZ2SevKa10saGaJPGCJ8vh3lKrC4u663zYEeRZxep+VbG6YRDtQ6gVqw9dYzPA==
+ dependencies:
+ "@types/sinonjs__fake-timers" "*"
+
+"@types/sinon@7.5.1":
+ version "7.5.1"
+ resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.5.1.tgz#d27b81af0d1cfe1f9b24eebe7a24f74ae40f5b7c"
+ integrity sha512-EZQUP3hSZQyTQRfiLqelC9NMWd1kqLcmQE0dMiklxBkgi84T+cHOhnKpgk4NnOWpGX863yE6+IaGnOXUNFqDnQ==
+
+"@types/sinonjs__fake-timers@*":
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz#681df970358c82836b42f989188d133e218c458e"
+ integrity sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA==
+
+"@types/sizzle@*", "@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==
@@ -1431,6 +1539,16 @@ 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-table3@0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202"
+ integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==
+ dependencies:
+ object-assign "^4.1.0"
+ string-width "^2.1.1"
+ optionalDependencies:
+ colors "^1.1.2"
+
cli-truncate@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574"
@@ -1513,6 +1631,11 @@ color@^3.0.0:
color-convert "^1.9.1"
color-string "^1.5.2"
+colors@^1.1.2:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
+ integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
+
combined-stream@^1.0.6, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
@@ -1550,7 +1673,7 @@ concat-map@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.2, 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==
@@ -1847,26 +1970,38 @@ cssstyle@^1.1.1:
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==
+cypress@4.4.1:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/cypress/-/cypress-4.4.1.tgz#f5aa1aa5f328f1299bff328103f7cbad89e80f29"
+ integrity sha512-LcskZ/PXRG9XTlEeeenKqz/KddT1x+7O7dqXsdKWPII01LxLNmNHIvHnlUqApchVbinJ5vir6J255CkELSeL0A==
dependencies:
"@cypress/listr-verbose-renderer" "0.4.1"
+ "@cypress/request" "2.88.5"
"@cypress/xvfb" "1.2.4"
+ "@types/blob-util" "1.3.3"
+ "@types/bluebird" "3.5.29"
+ "@types/chai" "4.2.7"
+ "@types/chai-jquery" "1.1.40"
+ "@types/jquery" "3.3.31"
+ "@types/lodash" "4.14.149"
+ "@types/minimatch" "3.0.3"
+ "@types/mocha" "5.2.7"
+ "@types/sinon" "7.5.1"
+ "@types/sinon-chai" "3.2.3"
"@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"
+ cli-table3 "0.5.1"
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"
+ extract-zip "1.7.0"
fs-extra "8.1.0"
getos "3.1.4"
is-ci "2.0.0"
@@ -1875,12 +2010,11 @@ cypress@4.1.0:
listr "0.14.3"
lodash "4.17.15"
log-symbols "3.0.0"
- minimist "1.2.0"
+ minimist "1.2.5"
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"
@@ -1917,7 +2051,7 @@ deasync@^0.1.14:
bindings "^1.5.0"
node-addon-api "^1.7.1"
-debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
+debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
@@ -2374,15 +2508,15 @@ 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=
+extract-zip@1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927"
+ integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==
dependencies:
- concat-stream "1.6.2"
- debug "2.6.9"
- mkdirp "0.5.1"
- yauzl "2.4.1"
+ concat-stream "^1.6.2"
+ debug "^2.6.9"
+ mkdirp "^0.5.4"
+ yauzl "^2.10.0"
extsprintf@1.3.0:
version "1.3.0"
@@ -2436,13 +2570,6 @@ 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"
@@ -2658,7 +2785,7 @@ har-schema@^2.0.0:
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
-har-validator@~5.1.0:
+har-validator@~5.1.0, har-validator@~5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
@@ -3613,7 +3740,12 @@ minimist@0.0.8:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
-minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0:
+minimist@1.2.5, minimist@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
+ integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+
+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=
@@ -3626,13 +3758,20 @@ mixin-deep@^1.2.0:
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"
+mkdirp@^0.5.4:
+ version "0.5.5"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
+ integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
+ dependencies:
+ minimist "^1.2.5"
+
moment@2.24.0:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
@@ -4865,7 +5004,7 @@ request-promise-native@^1.0.5, request-promise-native@^1.0.8:
stealthy-require "^1.1.1"
tough-cookie "^2.3.3"
-request@2.88.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==
@@ -5591,7 +5730,7 @@ toidentifier@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:
+tough-cookie@^2.3.3, tough-cookie@^2.5.0, 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==
@@ -5994,17 +6133,10 @@ yargs@^14.0.0:
y18n "^4.0.0"
yargs-parser "^15.0.0"
-yauzl@2.10.0:
+yauzl@2.10.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"