@depup/react-hook-form 7.87.0-depup.0 → 7.89.0-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +2 -2
  2. package/changes.json +1 -1
  3. package/dist/constants.d.ts +2 -0
  4. package/dist/constants.d.ts.map +1 -1
  5. package/dist/controller.d.ts +7 -35
  6. package/dist/controller.d.ts.map +1 -1
  7. package/dist/errorMessage.d.ts +27 -0
  8. package/dist/errorMessage.d.ts.map +1 -0
  9. package/dist/form.d.ts +7 -15
  10. package/dist/form.d.ts.map +1 -1
  11. package/dist/index.cjs.js +1 -1
  12. package/dist/index.cjs.js.map +1 -1
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.esm.mjs +390 -322
  16. package/dist/index.esm.mjs.map +1 -1
  17. package/dist/index.react-server.d.ts +3 -0
  18. package/dist/index.react-server.d.ts.map +1 -0
  19. package/dist/index.umd.js +1 -1
  20. package/dist/index.umd.js.map +1 -1
  21. package/dist/logic/createFormControl.d.ts.map +1 -1
  22. package/dist/logic/getResolverOptions.d.ts +1 -0
  23. package/dist/logic/getResolverOptions.d.ts.map +1 -1
  24. package/dist/logic/iterateFieldsByAction.d.ts +1 -1
  25. package/dist/logic/iterateFieldsByAction.d.ts.map +1 -1
  26. package/dist/logic/schemaErrorLookup.d.ts.map +1 -1
  27. package/dist/logic/validateField.d.ts.map +1 -1
  28. package/dist/react-server.esm.mjs +2535 -0
  29. package/dist/react-server.esm.mjs.map +1 -0
  30. package/dist/types/fields.d.ts +1 -0
  31. package/dist/types/fields.d.ts.map +1 -1
  32. package/dist/useController.d.ts +5 -17
  33. package/dist/useController.d.ts.map +1 -1
  34. package/dist/useFieldArray.d.ts +5 -30
  35. package/dist/useFieldArray.d.ts.map +1 -1
  36. package/dist/useForm.d.ts +7 -22
  37. package/dist/useForm.d.ts.map +1 -1
  38. package/dist/useFormContext.d.ts +10 -46
  39. package/dist/useFormContext.d.ts.map +1 -1
  40. package/dist/useFormState.d.ts +4 -23
  41. package/dist/useFormState.d.ts.map +1 -1
  42. package/dist/useWatch.d.ts +7 -140
  43. package/dist/useWatch.d.ts.map +1 -1
  44. package/dist/utils/cloneObject.d.ts.map +1 -1
  45. package/dist/utils/flatten.d.ts.map +1 -1
  46. package/dist/utils/formData.d.ts.map +1 -1
  47. package/dist/watch.d.ts +4 -16
  48. package/dist/watch.d.ts.map +1 -1
  49. package/package.json +12 -6
@@ -55,8 +55,9 @@ function cloneObject(data) {
55
55
  if (data instanceof Date) {
56
56
  return new Date(data);
57
57
  }
58
+ const isBlobInstance = typeof Blob !== 'undefined' && data instanceof Blob;
58
59
  const isFileListInstance = typeof FileList !== 'undefined' && data instanceof FileList;
59
- if (isWeb && (data instanceof Blob || isFileListInstance)) {
60
+ if (isWeb && (isBlobInstance || isFileListInstance)) {
60
61
  return data;
61
62
  }
62
63
  const isArray = Array.isArray(data);
@@ -96,6 +97,16 @@ const INPUT_VALIDATION_RULES = {
96
97
  required: 'required',
97
98
  validate: 'validate',
98
99
  };
100
+ const REGISTER_VALIDATION_RULES = [
101
+ INPUT_VALIDATION_RULES.required,
102
+ INPUT_VALIDATION_RULES.min,
103
+ INPUT_VALIDATION_RULES.max,
104
+ INPUT_VALIDATION_RULES.minLength,
105
+ INPUT_VALIDATION_RULES.maxLength,
106
+ INPUT_VALIDATION_RULES.pattern,
107
+ INPUT_VALIDATION_RULES.validate,
108
+ ];
109
+ const FORM_ERROR_TYPE = 'form';
99
110
  const ROOT_ERROR_TYPE = 'root';
100
111
  const PROTOTYPE_KEYWORDS = ['__proto__', 'constructor', 'prototype'];
101
112
 
@@ -274,33 +285,14 @@ function useResyncOnReconnect(getInitialValue) {
274
285
  }
275
286
 
276
287
  /**
277
- * Subscribes to each form state and isolates re-renders at the custom hook level. It has its own scope for form state subscriptions, so it will not affect other useFormState or useForm instances. Using this hook can reduce the re-render impact on large and complex form applications.
278
- *
279
- * @remarks
280
- * [API](https://react-hook-form.com/docs/useformstate) • [Demo](https://codesandbox.io/s/useformstate-75xly)
288
+ * Subscribes to form state with re-renders isolated to this hook.
289
+ * Optionally scope to specific field names to minimize re-render surface.
281
290
  *
282
- * @param props - Include options to specify fields to subscribe to. {@link UseFormStateReturn}
291
+ * @see [API](https://react-hook-form.com/docs/useformstate)
283
292
  *
284
293
  * @example
285
294
  * ```tsx
286
- * function App() {
287
- * const { register, handleSubmit, control } = useForm({
288
- * defaultValues: {
289
- * firstName: "firstName"
290
- * }});
291
- * const { dirtyFields } = useFormState({
292
- * control
293
- * });
294
- * const onSubmit = (data) => console.log(data);
295
- *
296
- * return (
297
- * <form onSubmit={handleSubmit(onSubmit)}>
298
- * <input {...register("firstName")} placeholder="First Name" />
299
- * {dirtyFields.firstName && <p>Field is dirty.</p>}
300
- * <input type="submit" />
301
- * </form>
302
- * );
303
- * }
295
+ * const { errors, isDirty } = useFormState({ control, name: "email" });
304
296
  * ```
305
297
  */
306
298
  function useFormState(props) {
@@ -341,7 +333,7 @@ function useFormState(props) {
341
333
  unsubscribe();
342
334
  snapshot(!disabled, getCurrentFormState);
343
335
  };
344
- }, [name, disabled, exact, resyncIfNeeded, snapshot]);
336
+ }, [control, name, disabled, exact, resyncIfNeeded, snapshot]);
345
337
  React.useEffect(() => {
346
338
  _localProxyFormState.current.isValid && control._setValid(true);
347
339
  }, [control]);
@@ -364,19 +356,15 @@ var generateWatchOutput = (names, _names, formValues, isGlobal, defaultValue) =>
364
356
  };
365
357
 
366
358
  /**
367
- * Custom hook to subscribe to field changes and isolate re-rendering at the component level.
359
+ * Subscribes to field value changes and isolates re-renders to the hook level.
368
360
  *
369
- * @remarks
370
- *
371
- * [API](https://react-hook-form.com/docs/usewatch) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-usewatch-h9i5e)
361
+ * @see [API](https://react-hook-form.com/docs/usewatch)
372
362
  *
373
363
  * @example
374
364
  * ```tsx
375
- * const { control } = useForm();
376
- * const values = useWatch({
377
- * name: "fieldName",
378
- * control,
379
- * })
365
+ * const email = useWatch({ control, name: "email" });
366
+ * const all = useWatch({ control });
367
+ * const adult = useWatch({ control, name: "age", compute: (v) => v >= 18 });
380
368
  * ```
381
369
  */
382
370
  function useWatch(props) {
@@ -384,7 +372,6 @@ function useWatch(props) {
384
372
  const { control = formControl, name, defaultValue, disabled, exact, compute, } = props || {};
385
373
  const _defaultValue = React.useRef(defaultValue);
386
374
  const _compute = React.useRef(compute);
387
- const _computeFormValues = React.useRef(undefined);
388
375
  const _prevControl = React.useRef(control);
389
376
  const _prevName = React.useRef(name);
390
377
  _compute.current = compute;
@@ -393,6 +380,7 @@ function useWatch(props) {
393
380
  return _compute.current ? _compute.current(defaultValue) : defaultValue;
394
381
  };
395
382
  const [value, updateValue] = React.useState(getInitialOutput);
383
+ const _computeFormValues = React.useRef(value);
396
384
  const getCurrentOutput = React.useCallback((values) => {
397
385
  const formValues = generateWatchOutput(name, control._names, values || control._formValues, false, _defaultValue.current);
398
386
  return _compute.current ? _compute.current(formValues) : formValues;
@@ -466,27 +454,15 @@ function useWatch(props) {
466
454
  }
467
455
 
468
456
  /**
469
- * Custom hook to work with controlled component, this function provide you with both form and field level state. Re-render is isolated at the hook level.
470
- *
471
- * @remarks
472
- * [API](https://react-hook-form.com/docs/usecontroller) • [Demo](https://codesandbox.io/s/usecontroller-0o8px)
473
- *
474
- * @param props - the path name to the form field value, and validation rules.
457
+ * Hook for controlled inputs. Returns `field`, `fieldState`, and `formState`.
458
+ * Re-renders are isolated to the hook level.
475
459
  *
476
- * @returns field properties, field and form state. {@link UseControllerReturn}
460
+ * @see [API](https://react-hook-form.com/docs/usecontroller)
477
461
  *
478
462
  * @example
479
463
  * ```tsx
480
- * function Input(props) {
481
- * const { field, fieldState, formState } = useController(props);
482
- * return (
483
- * <div>
484
- * <input {...field} placeholder={props.name} />
485
- * <p>{fieldState.isTouched && "Touched"}</p>
486
- * <p>{formState.isSubmitted ? "submitted" : ""}</p>
487
- * </div>
488
- * );
489
- * }
464
+ * const { field, fieldState } = useController({ control, name: "email" });
465
+ * return <input {...field} />;
490
466
  * ```
491
467
  */
492
468
  function useController(props) {
@@ -575,6 +551,7 @@ function useController(props) {
575
551
  const field = get(control._fields, name);
576
552
  if (field && field._f && elm) {
577
553
  field._f.ref = _proxyRef.current;
554
+ field._f._c = true;
578
555
  }
579
556
  }, [control._fields, name]);
580
557
  const field = React.useMemo(() => ({
@@ -599,6 +576,7 @@ function useController(props) {
599
576
  const field = get(control._fields, name);
600
577
  if (field && field._f) {
601
578
  field._f.mount = value;
579
+ field._f._c = value;
602
580
  }
603
581
  };
604
582
  updateMounted(name, true);
@@ -611,7 +589,13 @@ function useController(props) {
611
589
  set(control._formValues, name, value);
612
590
  }
613
591
  }
614
- !isArrayField && control.register(name);
592
+ !isArrayField &&
593
+ control.register(name, {
594
+ ..._props.current.rules,
595
+ ...(isBoolean(_props.current.disabled)
596
+ ? { disabled: _props.current.disabled }
597
+ : {}),
598
+ });
615
599
  if (_proxyRef.current) {
616
600
  const field = get(control._fields, name);
617
601
  if (field && field._f) {
@@ -640,48 +624,50 @@ function useController(props) {
640
624
  }
641
625
 
642
626
  /**
643
- * Component based on `useController` hook to work with controlled component.
627
+ * Component wrapper around `useController` for controlled inputs.
644
628
  *
645
- * @remarks
646
- * [API](https://react-hook-form.com/docs/usecontroller/controller) • [Demo](https://codesandbox.io/s/react-hook-form-v6-controller-ts-jwyzw) • [Video](https://www.youtube.com/watch?v=N2UNk_UCVyA)
629
+ * @see [API](https://react-hook-form.com/docs/usecontroller/controller)
647
630
  *
648
- * @param props - the path name to the form field value, and validation rules.
631
+ * @example
632
+ * ```tsx
633
+ * <Controller
634
+ * control={control}
635
+ * name="test"
636
+ * render={({ field, fieldState, formState }) => <input {...field} />}
637
+ * />
638
+ * ```
639
+ */
640
+ const Controller = (props) => props.render(useController(props));
641
+
642
+ /**
643
+ * Displays the validation error for a single field.
644
+ * Reads from `control` when provided, otherwise from the nearest `FormProvider`.
649
645
  *
650
- * @returns provide field handler functions, field and form state.
646
+ * @see [API](https://react-hook-form.com/docs/useformstate/errormessage)
651
647
  *
652
648
  * @example
653
649
  * ```tsx
654
- * function App() {
655
- * const { control } = useForm<FormValues>({
656
- * defaultValues: {
657
- * test: ""
658
- * }
659
- * });
660
- *
661
- * return (
662
- * <form>
663
- * <Controller
664
- * control={control}
665
- * name="test"
666
- * render={({ field: { onChange, onBlur, value, ref }, formState, fieldState }) => (
667
- * <>
668
- * <input
669
- * onChange={onChange} // send value to hook form
670
- * onBlur={onBlur} // notify when input is touched
671
- * value={value} // return updated value
672
- * ref={ref} // set ref for focus management
673
- * />
674
- * <p>{formState.isSubmitted ? "submitted" : ""}</p>
675
- * <p>{fieldState.isTouched ? "touched" : ""}</p>
676
- * </>
677
- * )}
678
- * />
679
- * </form>
680
- * );
681
- * }
650
+ * <ErrorMessage control={control} name="email" as="p" />
651
+ * <ErrorMessage name="email" as="span" />
652
+ * <ErrorMessage control={control} name="email"
653
+ * render={({ message }) => <Alert>{message}</Alert>} />
682
654
  * ```
683
655
  */
684
- const Controller = (props) => props.render(useController(props));
656
+ const ErrorMessage = ({ as, control, name, render, }) => {
657
+ const { errors } = useFormState({
658
+ control,
659
+ name: name,
660
+ });
661
+ const error = get(errors, name);
662
+ if (!error) {
663
+ return null;
664
+ }
665
+ const message = error.message || '';
666
+ if (render) {
667
+ return render({ message, messages: error.types });
668
+ }
669
+ return React.createElement(as || React.Fragment, null, message);
670
+ };
685
671
 
686
672
  var generateId = () => typeof crypto !== 'undefined' && crypto.randomUUID
687
673
  ? crypto.randomUUID()
@@ -715,7 +701,7 @@ var isWatched = (name, _names, isBlurEvent) => {
715
701
  return false;
716
702
  };
717
703
 
718
- const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
704
+ const iterateFieldsByAction = (fields, action, fieldsNames) => {
719
705
  for (const key of fieldsNames || Object.keys(fields)) {
720
706
  if (key === '_f') {
721
707
  continue;
@@ -724,21 +710,21 @@ const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
724
710
  if (field) {
725
711
  const { _f } = field;
726
712
  if (_f) {
727
- if (_f.refs && _f.refs[0] && action(_f.refs[0], key) && !abortEarly) {
713
+ if (_f.refs && _f.refs[0] && action(_f.refs[0], _f.name)) {
728
714
  return true;
729
715
  }
730
- else if (_f.ref && action(_f.ref, _f.name) && !abortEarly) {
716
+ else if (_f.ref && action(_f.ref, _f.name)) {
731
717
  return true;
732
718
  }
733
719
  else {
734
720
  if (iterateFieldsByAction(field, action)) {
735
- break;
721
+ return true;
736
722
  }
737
723
  }
738
724
  }
739
725
  else if (isObject(field) || Array.isArray(field)) {
740
726
  if (iterateFieldsByAction(field, action)) {
741
- break;
727
+ return true;
742
728
  }
743
729
  }
744
730
  }
@@ -839,7 +825,7 @@ var getValueAndMessage = (validationData) => isObject(validationData) && !isRege
839
825
  };
840
826
 
841
827
  var validateField = async (field, disabledFieldNames, formValues, validateAllFieldCriteria, shouldUseNativeValidation, isFieldArray) => {
842
- const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, } = field._f;
828
+ const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, _c, } = field._f;
843
829
  const inputValue = get(formValues, name);
844
830
  if (!mount || disabledFieldNames.has(name)) {
845
831
  return {};
@@ -849,7 +835,8 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
849
835
  if (shouldUseNativeValidation && inputRef.reportValidity) {
850
836
  const validityMessage = isBoolean(message) ? '' : message || '';
851
837
  if (refs) {
852
- refs.forEach((ref) => ref.setCustomValidity(validityMessage));
838
+ refs.forEach((ref) => isFunction(ref.setCustomValidity) &&
839
+ ref.setCustomValidity(validityMessage));
853
840
  }
854
841
  else {
855
842
  inputRef.setCustomValidity(validityMessage);
@@ -864,7 +851,7 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
864
851
  const isEmpty = ((valueAsNumber || isFileInput(ref)) &&
865
852
  isUndefined(ref.value) &&
866
853
  isUndefined(inputValue)) ||
867
- (isHTMLElement(ref) && ref.value === '') ||
854
+ (isHTMLElement(ref) && ref.value === '' && !_c) ||
868
855
  inputValue === '' ||
869
856
  (Array.isArray(inputValue) && !inputValue.length);
870
857
  const appendErrorsCurry = appendErrors.bind(null, name, validateAllFieldCriteria, error);
@@ -1004,7 +991,9 @@ var validateField = async (field, disabledFieldNames, formValues, validateAllFie
1004
991
  ...validateError,
1005
992
  ...appendErrorsCurry(key, validateError.message),
1006
993
  };
1007
- setCustomValidity(validateError.message);
994
+ if (!validateAllFieldCriteria) {
995
+ setCustomValidity(validateError.message);
996
+ }
1008
997
  if (validateAllFieldCriteria) {
1009
998
  error[name] = validationResult;
1010
999
  }
@@ -1132,48 +1121,27 @@ var updateAt = (fieldValues, index, value) => {
1132
1121
  };
1133
1122
 
1134
1123
  /**
1135
- * A custom hook that exposes convenient methods to perform operations with a list of dynamic inputs that need to be appended, updated, removed etc. • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn) • [Video](https://youtu.be/4MrbfGSFY2A)
1136
- *
1137
- * @remarks
1138
- * [API](https://react-hook-form.com/docs/usefieldarray) • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn)
1139
- *
1140
- * @param props - useFieldArray props
1124
+ * Hook for dynamic field arrays. Provides `fields` and mutation methods:
1125
+ * `append`, `prepend`, `remove`, `insert`, `swap`, `move`, `update`, `replace`.
1141
1126
  *
1142
- * @returns methods - functions to manipulate with the Field Arrays (dynamic inputs) {@link UseFieldArrayReturn}
1127
+ * @see [API](https://react-hook-form.com/docs/usefieldarray)
1143
1128
  *
1144
1129
  * @example
1145
1130
  * ```tsx
1146
- * function App() {
1147
- * const { register, control, handleSubmit, reset, trigger, setError } = useForm({
1148
- * defaultValues: {
1149
- * test: []
1150
- * }
1151
- * });
1152
- * const { fields, append } = useFieldArray({
1153
- * control,
1154
- * name: "test"
1155
- * });
1156
- *
1157
- * return (
1158
- * <form onSubmit={handleSubmit(data => console.log(data))}>
1159
- * {fields.map((item, index) => (
1160
- * <input key={item.id} {...register(`test.${index}.firstName`)} />
1161
- * ))}
1162
- * <button type="button" onClick={() => append({ firstName: "bill" })}>
1163
- * append
1164
- * </button>
1165
- * <input type="submit" />
1166
- * </form>
1167
- * );
1168
- * }
1131
+ * const { fields, append } = useFieldArray({ control, name: "items" });
1132
+ * return fields.map((f, i) => <input key={f.id} {...register(`items.${i}.name`)} />);
1169
1133
  * ```
1170
1134
  */
1171
1135
  function useFieldArray(props) {
1172
1136
  const formControl = useFormControlContext();
1173
1137
  const { control = formControl, name, keyName = 'id', disabled, shouldUnregister, rules, } = props;
1174
- const [fields, setFields] = React.useState(control._getFieldArray(name));
1138
+ const getCurrentFieldArray = () => control._getFieldArray(name);
1139
+ const [fields, setFields] = React.useState(getCurrentFieldArray);
1175
1140
  const ids = React.useRef(control._getFieldArray(name).map(generateId));
1176
1141
  const _actioned = React.useRef(false);
1142
+ const { resyncIfNeeded, snapshot } = useResyncOnReconnect(getCurrentFieldArray);
1143
+ const _prevControl = React.useRef(control);
1144
+ const _prevName = React.useRef(name);
1177
1145
  if (!disabled) {
1178
1146
  control._names.array.add(name);
1179
1147
  }
@@ -1185,7 +1153,23 @@ function useFieldArray(props) {
1185
1153
  if (disabled) {
1186
1154
  return;
1187
1155
  }
1188
- return control._subjects.array.subscribe({
1156
+ if (_prevControl.current === control && _prevName.current === name) {
1157
+ resyncIfNeeded(true, getCurrentFieldArray, (fieldValues) => {
1158
+ setFields(fieldValues);
1159
+ ids.current = fieldValues.map(generateId);
1160
+ });
1161
+ }
1162
+ else {
1163
+ _prevControl.current = control;
1164
+ _prevName.current = name;
1165
+ const fieldValues = getCurrentFieldArray();
1166
+ if (!deepEqual(fields, fieldValues)) {
1167
+ setFields(fieldValues);
1168
+ ids.current = fieldValues.map(generateId);
1169
+ }
1170
+ snapshot(true, getCurrentFieldArray);
1171
+ }
1172
+ const unsubscribe = control._subjects.array.subscribe({
1189
1173
  next: ({ values, name: fieldArrayName, }) => {
1190
1174
  if (fieldArrayName === name || !fieldArrayName) {
1191
1175
  const fieldValues = get(values, name);
@@ -1200,7 +1184,11 @@ function useFieldArray(props) {
1200
1184
  }
1201
1185
  },
1202
1186
  }).unsubscribe;
1203
- }, [control, name, disabled]);
1187
+ return () => {
1188
+ unsubscribe();
1189
+ snapshot(true, getCurrentFieldArray);
1190
+ };
1191
+ }, [control, name, disabled, resyncIfNeeded, snapshot]);
1204
1192
  const updateValues = React.useCallback((updatedFieldArrayValues) => {
1205
1193
  _actioned.current = true;
1206
1194
  control._setFieldArray(name, updatedFieldArrayValues);
@@ -1312,7 +1300,9 @@ function useFieldArray(props) {
1312
1300
  ids.current = updatedFieldArrayValues.map(generateId);
1313
1301
  updateValues([...updatedFieldArrayValues]);
1314
1302
  setFields([...updatedFieldArrayValues]);
1315
- control._setFieldArray(name, [...updatedFieldArrayValues], (data) => data, {}, true, false);
1303
+ control._setFieldArray(name, [...updatedFieldArrayValues], (data) => Array.isArray(data)
1304
+ ? data.slice(0, updatedFieldArrayValues.length)
1305
+ : data, {});
1316
1306
  };
1317
1307
  React.useEffect(() => {
1318
1308
  if (disabled) {
@@ -1363,10 +1353,23 @@ function useFieldArray(props) {
1363
1353
  else {
1364
1354
  const field = get(control._fields, name);
1365
1355
  if (field && field._f) {
1366
- validateField(field, control._names.disabled, control._formValues, control._options.criteriaMode === VALIDATION_MODE.all, control._options.shouldUseNativeValidation, true).then((error) => !isEmptyObject(error) &&
1367
- control._subjects.state.next({
1368
- errors: updateFieldArrayRootError(control._formState.errors, error, name),
1369
- }));
1356
+ validateField(field, control._names.disabled, control._formValues, control._options.criteriaMode === VALIDATION_MODE.all, control._options.shouldUseNativeValidation, true).then((error) => {
1357
+ if (!isEmptyObject(error)) {
1358
+ control._subjects.state.next({
1359
+ errors: updateFieldArrayRootError(control._formState.errors, error, name),
1360
+ });
1361
+ }
1362
+ else {
1363
+ const existingError = get(control._formState.errors, name);
1364
+ if (existingError && existingError[ROOT_ERROR_TYPE]) {
1365
+ unset(control._formState.errors, `${name}.${ROOT_ERROR_TYPE}`);
1366
+ control._subjects.state.next({
1367
+ errors: control._formState
1368
+ .errors,
1369
+ });
1370
+ }
1371
+ }
1372
+ });
1370
1373
  }
1371
1374
  }
1372
1375
  }
@@ -1394,7 +1397,8 @@ function useFieldArray(props) {
1394
1397
  }, [fields, name, control, disabled]);
1395
1398
  React.useEffect(() => {
1396
1399
  if (!disabled) {
1397
- !get(control._formValues, name) && control._setFieldArray(name);
1400
+ isUndefined(get(control._formValues, name)) &&
1401
+ control._setFieldArray(name);
1398
1402
  }
1399
1403
  return () => {
1400
1404
  control._state.actionArrayLengths.delete(name);
@@ -1482,13 +1486,15 @@ const FieldArray = (props) => props.render(useFieldArray(props));
1482
1486
 
1483
1487
  const isFileLike = (value) => (typeof Blob !== 'undefined' && value instanceof Blob) ||
1484
1488
  (typeof File !== 'undefined' && value instanceof File);
1489
+ const isFileListLike = (value) => typeof FileList !== 'undefined' && value instanceof FileList;
1485
1490
  const flatten = (obj) => {
1486
1491
  const output = {};
1487
1492
  for (const key of Object.keys(obj)) {
1488
1493
  if (isObjectType(obj[key]) &&
1489
1494
  obj[key] !== null &&
1490
1495
  !isDateObject(obj[key]) &&
1491
- !isFileLike(obj[key])) {
1496
+ !isFileLike(obj[key]) &&
1497
+ !isFileListLike(obj[key])) {
1492
1498
  const nested = flatten(obj[key]);
1493
1499
  for (const nestedKey of Object.keys(nested)) {
1494
1500
  output[`${key}.${nestedKey}`] = nested[nestedKey];
@@ -1505,7 +1511,18 @@ function jsonToFormData(json) {
1505
1511
  const result = new FormData();
1506
1512
  const flattenFormValues = flatten(json);
1507
1513
  for (const key in flattenFormValues) {
1508
- result.append(key, flattenFormValues[key]);
1514
+ const value = flattenFormValues[key];
1515
+ if (isUndefined(value)) {
1516
+ continue;
1517
+ }
1518
+ if (typeof FileList !== 'undefined' && value instanceof FileList) {
1519
+ for (let index = 0; index < value.length; index++) {
1520
+ const file = value[index];
1521
+ file && result.append(key, file);
1522
+ }
1523
+ continue;
1524
+ }
1525
+ result.append(key, value);
1509
1526
  }
1510
1527
  return result;
1511
1528
  }
@@ -1524,64 +1541,28 @@ function noop() { }
1524
1541
  const HookFormContext = React.createContext(null);
1525
1542
  HookFormContext.displayName = 'HookFormContext';
1526
1543
  /**
1527
- * This custom hook allows you to access the form context. useFormContext is intended to be used in deeply nested structures, where it would become inconvenient to pass the context as a prop. To be used with {@link FormProvider}.
1528
- *
1529
- * @remarks
1530
- * [API](https://react-hook-form.com/docs/useformcontext) • [Demo](https://codesandbox.io/s/react-hook-form-v7-form-context-ytudi)
1544
+ * Retrieves all `useForm` methods from the nearest `FormProvider`.
1545
+ * Use in deeply nested components to avoid prop-drilling.
1531
1546
  *
1532
- * @returns return all useForm methods
1547
+ * @see [API](https://react-hook-form.com/docs/useformcontext)
1533
1548
  *
1534
1549
  * @example
1535
1550
  * ```tsx
1536
- * function App() {
1537
- * const methods = useForm();
1538
- * const onSubmit = data => console.log(data);
1539
- *
1540
- * return (
1541
- * <FormProvider {...methods} >
1542
- * <form onSubmit={methods.handleSubmit(onSubmit)}>
1543
- * <NestedInput />
1544
- * <input type="submit" />
1545
- * </form>
1546
- * </FormProvider>
1547
- * );
1548
- * }
1549
- *
1550
- * function NestedInput() {
1551
- * const { register } = useFormContext(); // retrieve all hook methods
1552
- * return <input {...register("test")} />;
1553
- * }
1551
+ * const { register } = useFormContext<FormValues>();
1554
1552
  * ```
1555
1553
  */
1556
1554
  const useFormContext = () => React.useContext(HookFormContext);
1557
1555
  /**
1558
- * A provider component that propagates the `useForm` methods to all children components via [React Context](https://react.dev/reference/react/useContext) API. To be used with {@link useFormContext}.
1559
- *
1560
- * @remarks
1561
- * [API](https://react-hook-form.com/docs/useformcontext) • [Demo](https://codesandbox.io/s/react-hook-form-v7-form-context-ytudi)
1556
+ * Provides all `useForm` methods to the component tree via React Context.
1557
+ * Pair with `useFormContext` to consume them in any descendant.
1562
1558
  *
1563
- * @param props - all useForm methods
1559
+ * @see [API](https://react-hook-form.com/docs/useformcontext)
1564
1560
  *
1565
1561
  * @example
1566
1562
  * ```tsx
1567
- * function App() {
1568
- * const methods = useForm();
1569
- * const onSubmit = data => console.log(data);
1570
- *
1571
- * return (
1572
- * <FormProvider {...methods} >
1573
- * <form onSubmit={methods.handleSubmit(onSubmit)}>
1574
- * <NestedInput />
1575
- * <input type="submit" />
1576
- * </form>
1577
- * </FormProvider>
1578
- * );
1579
- * }
1580
- *
1581
- * function NestedInput() {
1582
- * const { register } = useFormContext(); // retrieve all hook methods
1583
- * return <input {...register("test")} />;
1584
- * }
1563
+ * <FormProvider {...methods}>
1564
+ * <form onSubmit={methods.handleSubmit(onSubmit)}>{children}</form>
1565
+ * </FormProvider>
1585
1566
  * ```
1586
1567
  */
1587
1568
  const FormProvider = ({ children, watch, getValues, getErrors, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, resetDefaultValues, handleSubmit, unregister, control, register, setFocus, subscribe, }) => {
@@ -1635,25 +1616,17 @@ function defaultValidateStatus(status) {
1635
1616
  return status >= 200 && status < 300;
1636
1617
  }
1637
1618
  /**
1638
- * Form component to manage submission.
1639
- *
1640
- * @param props - to setup submission detail. {@link FormProps}
1619
+ * Form component that handles submission, including optional `action` fetch and server error wiring.
1641
1620
  *
1642
- * @returns form component or headless render prop.
1621
+ * @see [API](https://react-hook-form.com/docs/useform/form)
1643
1622
  *
1644
1623
  * @example
1645
1624
  * ```tsx
1646
- * function App() {
1647
- * const { control, formState: { errors } } = useForm();
1648
- *
1649
- * return (
1650
- * <Form action="/api" control={control}>
1651
- * <input {...register("name")} />
1652
- * <p>{errors?.root?.server && 'Server error'}</p>
1653
- * <button>Submit</button>
1654
- * </Form>
1655
- * );
1656
- * }
1625
+ * <Form action="/api" control={control}>
1626
+ * <input {...register("name")} />
1627
+ * <p>{errors?.root?.server && 'Server error'}</p>
1628
+ * <button>Submit</button>
1629
+ * </Form>
1657
1630
  * ```
1658
1631
  */
1659
1632
  function Form(props) {
@@ -1778,16 +1751,15 @@ var createSubject = () => {
1778
1751
  };
1779
1752
 
1780
1753
  function extractFormValues(fieldsState, formValues) {
1781
- const values = {};
1754
+ const values = (Array.isArray(fieldsState) ? [] : {});
1782
1755
  for (const key in fieldsState) {
1783
1756
  if (fieldsState.hasOwnProperty(key)) {
1784
1757
  const fieldState = fieldsState[key];
1785
1758
  const fieldValue = formValues[key];
1786
- if (fieldState && isObject(fieldState) && fieldValue) {
1787
- const nestedFieldsState = extractFormValues(fieldState, fieldValue);
1788
- if (isObject(nestedFieldsState)) {
1789
- values[key] = nestedFieldsState;
1790
- }
1759
+ if (fieldState &&
1760
+ (isObject(fieldState) || Array.isArray(fieldState)) &&
1761
+ fieldValue) {
1762
+ values[key] = extractFormValues(fieldState, fieldValue);
1791
1763
  }
1792
1764
  else if (fieldsState[key]) {
1793
1765
  values[key] = fieldValue;
@@ -2011,7 +1983,7 @@ var hasValidation = (options) => options.mount &&
2011
1983
 
2012
1984
  function schemaErrorLookup(errors, _fields, name) {
2013
1985
  const error = get(errors, name);
2014
- if (error || isKey(name)) {
1986
+ if ((error === null || error === void 0 ? void 0 : error.type) || (error === null || error === void 0 ? void 0 : error.message) || Array.isArray(error)) {
2015
1987
  return {
2016
1988
  error,
2017
1989
  name,
@@ -2090,7 +2062,6 @@ const defaultOptions = {
2090
2062
  reValidateMode: VALIDATION_MODE.onChange,
2091
2063
  shouldFocusError: true,
2092
2064
  };
2093
- const FORM_ERROR_TYPE = 'form';
2094
2065
  const updateDirtyFields = (dirtyFields, nextDirtyFields) => {
2095
2066
  for (const key in dirtyFields) {
2096
2067
  if (!(key in nextDirtyFields)) {
@@ -2165,24 +2136,35 @@ function createFormControl(props = {}) {
2165
2136
  let _proxySubscribeFormState = {
2166
2137
  ..._proxyFormState,
2167
2138
  };
2139
+ const _isTracked = (...keys) => keys.some((key) => _proxyFormState[key] || _proxySubscribeFormState[key]);
2168
2140
  const _subjects = {
2169
2141
  array: createSubject(),
2170
2142
  state: createSubject(),
2171
2143
  };
2172
2144
  let _setValidCallId = 0;
2173
- const shouldDisplayAllAssociatedErrors = _options.criteriaMode === VALIDATION_MODE.all;
2145
+ let _resetCallId = 0;
2146
+ let shouldDisplayAllAssociatedErrors = _options.criteriaMode === VALIDATION_MODE.all;
2174
2147
  const debounce = (name, callback) => (wait) => {
2175
2148
  clearTimeout(timers[name]);
2176
2149
  timers[name] = setTimeout(callback, wait);
2177
2150
  };
2151
+ const cancelDelayedError = (name) => {
2152
+ clearTimeout(timers[name]);
2153
+ delete timers[name];
2154
+ delete delayErrorCallbacks[name];
2155
+ };
2156
+ const cancelDelayedErrorTree = (name) => {
2157
+ cancelDelayedError(name);
2158
+ const prefix = `${name}.`;
2159
+ for (const key of Object.keys(delayErrorCallbacks)) {
2160
+ key.startsWith(prefix) && cancelDelayedError(key);
2161
+ }
2162
+ };
2178
2163
  const _setValid = async (shouldUpdateValid) => {
2179
2164
  if (_state.keepIsValid) {
2180
2165
  return;
2181
2166
  }
2182
- if (!_options.disabled &&
2183
- (_proxyFormState.isValid ||
2184
- _proxySubscribeFormState.isValid ||
2185
- shouldUpdateValid)) {
2167
+ if (!_options.disabled && (_isTracked('isValid') || shouldUpdateValid)) {
2186
2168
  const callId = ++_setValidCallId;
2187
2169
  let isValid;
2188
2170
  if (_options.resolver) {
@@ -2204,11 +2186,7 @@ function createFormControl(props = {}) {
2204
2186
  }
2205
2187
  };
2206
2188
  const _updateIsValidating = (names, isValidating) => {
2207
- if (!_options.disabled &&
2208
- (_proxyFormState.isValidating ||
2209
- _proxyFormState.validatingFields ||
2210
- _proxySubscribeFormState.isValidating ||
2211
- _proxySubscribeFormState.validatingFields)) {
2189
+ if (!_options.disabled && _isTracked('isValidating', 'validatingFields')) {
2212
2190
  (names || _names.mount).forEach((name) => {
2213
2191
  if (name) {
2214
2192
  isValidating
@@ -2247,20 +2225,26 @@ function createFormControl(props = {}) {
2247
2225
  unsetEmptyArray(_formState.errors, name);
2248
2226
  }
2249
2227
  const touchedFieldsArray = get(_formState.touchedFields, name);
2250
- if ((_proxyFormState.touchedFields ||
2251
- _proxySubscribeFormState.touchedFields) &&
2252
- shouldUpdateFieldsAndState &&
2253
- Array.isArray(touchedFieldsArray)) {
2228
+ const shouldUpdateTouchedFields = shouldUpdateFieldsAndState && Array.isArray(touchedFieldsArray);
2229
+ if (shouldUpdateTouchedFields) {
2254
2230
  const touchedFields = method(touchedFieldsArray, args.argA, args.argB);
2255
2231
  shouldSetValues && set(_formState.touchedFields, name, touchedFields);
2256
2232
  }
2257
- if (_proxyFormState.dirtyFields || _proxySubscribeFormState.dirtyFields) {
2233
+ const dirtyFieldsArray = get(_formState.dirtyFields, name);
2234
+ if (shouldUpdateFieldsAndState && Array.isArray(dirtyFieldsArray)) {
2235
+ const dirtyFields = method(dirtyFieldsArray, args.argA, args.argB) || dirtyFieldsArray;
2236
+ shouldSetValues && set(_formState.dirtyFields, name, dirtyFields);
2237
+ }
2238
+ if (_isTracked('dirtyFields')) {
2258
2239
  _updateDirtyFields();
2259
2240
  }
2260
2241
  _subjects.state.next({
2261
2242
  name,
2262
2243
  isDirty: _getDirty(name, values),
2263
2244
  dirtyFields: _formState.dirtyFields,
2245
+ ...(shouldUpdateTouchedFields && {
2246
+ touchedFields: _formState.touchedFields,
2247
+ }),
2264
2248
  errors: _formState.errors,
2265
2249
  isValid: _formState.isValid,
2266
2250
  });
@@ -2277,11 +2261,14 @@ function createFormControl(props = {}) {
2277
2261
  });
2278
2262
  };
2279
2263
  const _setErrors = (errors) => {
2264
+ Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
2265
+ const hasErrors = !isEmptyObject(errors);
2280
2266
  _formState.errors = errors;
2281
2267
  _subjects.state.next({
2282
2268
  errors: _formState.errors,
2283
- isValid: false,
2269
+ ...(hasErrors ? { isValid: false } : {}),
2284
2270
  });
2271
+ !hasErrors && _state.mount && _setValid();
2285
2272
  };
2286
2273
  const hasExplicitNullIntermediate = (name) => {
2287
2274
  const segments = isKey(name) ? [name] : stringToPath(name);
@@ -2299,7 +2286,7 @@ function createFormControl(props = {}) {
2299
2286
  }
2300
2287
  return false;
2301
2288
  };
2302
- const isStaleArrayIndex = (name) => {
2289
+ const isStaleArrayField = (name) => {
2303
2290
  if (!_state.actionArrayLengths.size) {
2304
2291
  return false;
2305
2292
  }
@@ -2326,13 +2313,19 @@ function createFormControl(props = {}) {
2326
2313
  ownerPreActionLength = _state.actionArrayLengths.get(path);
2327
2314
  }
2328
2315
  node = node[key];
2316
+ if (isUndefined(node) &&
2317
+ ownerDepth !== -1 &&
2318
+ i > ownerDepth &&
2319
+ +segments[ownerDepth] < ownerPreActionLength) {
2320
+ return true;
2321
+ }
2329
2322
  }
2330
2323
  return false;
2331
2324
  };
2332
2325
  const updateValidAndValue = (name, shouldSkipSetValueAs, value, ref) => {
2333
2326
  const field = get(_fields, name);
2334
2327
  if (field) {
2335
- if (hasExplicitNullIntermediate(name) || isStaleArrayIndex(name)) {
2328
+ if (hasExplicitNullIntermediate(name) || isStaleArrayField(name)) {
2336
2329
  return;
2337
2330
  }
2338
2331
  const wasUnsetInFormValues = isUndefined(get(_formValues, name));
@@ -2343,10 +2336,16 @@ function createFormControl(props = {}) {
2343
2336
  ? set(_formValues, name, shouldSkipSetValueAs ? defaultValue : getFieldValue(field._f))
2344
2337
  : setFieldValue(name, defaultValue);
2345
2338
  if (_state.mount && !_state.action) {
2346
- _setValid();
2339
+ if (_options.resolver &&
2340
+ _isTracked('isValidating', 'validatingFields')) {
2341
+ Promise.resolve().then(() => _setValid());
2342
+ }
2343
+ else {
2344
+ _setValid();
2345
+ }
2347
2346
  if (wasUnsetInFormValues &&
2348
2347
  _formState.isDirty &&
2349
- (_proxyFormState.isDirty || _proxySubscribeFormState.isDirty)) {
2348
+ _isTracked('isDirty')) {
2350
2349
  const isDirty = _getDirty();
2351
2350
  if (!isDirty) {
2352
2351
  _formState.isDirty = false;
@@ -2373,7 +2372,7 @@ function createFormControl(props = {}) {
2373
2372
  if (!_options.disabled || shouldDirty === true) {
2374
2373
  if (!isBlurEvent || shouldDirty) {
2375
2374
  const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
2376
- if (_proxyFormState.isDirty || _proxySubscribeFormState.isDirty) {
2375
+ if (_isTracked('isDirty')) {
2377
2376
  isPreviousDirty = _formState.isDirty;
2378
2377
  _formState.isDirty = output.isDirty =
2379
2378
  !isCurrentFieldPristine || _getDirty();
@@ -2391,8 +2390,7 @@ function createFormControl(props = {}) {
2391
2390
  output.dirtyFields = _formState.dirtyFields;
2392
2391
  shouldUpdateField =
2393
2392
  shouldUpdateField ||
2394
- ((_proxyFormState.dirtyFields ||
2395
- _proxySubscribeFormState.dirtyFields) &&
2393
+ (_isTracked('dirtyFields') &&
2396
2394
  isPreviousDirty !== !isCurrentFieldPristine);
2397
2395
  }
2398
2396
  if (isBlurEvent) {
@@ -2402,8 +2400,7 @@ function createFormControl(props = {}) {
2402
2400
  output.touchedFields = _formState.touchedFields;
2403
2401
  shouldUpdateField =
2404
2402
  shouldUpdateField ||
2405
- ((_proxyFormState.touchedFields ||
2406
- _proxySubscribeFormState.touchedFields) &&
2403
+ (_isTracked('touchedFields') &&
2407
2404
  isPreviousFieldTouched !== isBlurEvent);
2408
2405
  }
2409
2406
  }
@@ -2413,7 +2410,7 @@ function createFormControl(props = {}) {
2413
2410
  };
2414
2411
  const shouldRenderByError = (name, isValid, error, fieldState) => {
2415
2412
  const previousFieldError = get(_formState.errors, name);
2416
- const shouldUpdateValid = (_proxyFormState.isValid || _proxySubscribeFormState.isValid) &&
2413
+ const shouldUpdateValid = _isTracked('isValid') &&
2417
2414
  isBoolean(isValid) &&
2418
2415
  _formState.isValid !== isValid;
2419
2416
  if (_options.delayError && error) {
@@ -2421,8 +2418,7 @@ function createFormControl(props = {}) {
2421
2418
  delayErrorCallbacks[name](_options.delayError);
2422
2419
  }
2423
2420
  else {
2424
- clearTimeout(timers[name]);
2425
- delete delayErrorCallbacks[name];
2421
+ cancelDelayedError(name);
2426
2422
  error
2427
2423
  ? set(_formState.errors, name, error)
2428
2424
  : unset(_formState.errors, name);
@@ -2445,55 +2441,76 @@ function createFormControl(props = {}) {
2445
2441
  return await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
2446
2442
  };
2447
2443
  const executeSchemaAndUpdateState = async (names) => {
2444
+ const resetCallId = _resetCallId;
2448
2445
  const { errors } = await _runSchema(names);
2446
+ if (resetCallId !== _resetCallId) {
2447
+ return errors;
2448
+ }
2449
2449
  _updateIsValidating(names);
2450
2450
  if (names) {
2451
2451
  for (const name of names) {
2452
2452
  const error = get(errors, name);
2453
- error
2454
- ? _names.array.has(name) &&
2455
- isObject(error) &&
2456
- !Object.keys(error).some((key) => !Number.isNaN(Number(key)))
2457
- ? updateFieldArrayRootError(_formState.errors, { [name]: error }, name)
2458
- : set(_formState.errors, name, error)
2459
- : unset(_formState.errors, name);
2453
+ cancelDelayedErrorTree(name);
2454
+ const isFieldArrayRootError = _names.array.has(name) &&
2455
+ isObject(error) &&
2456
+ !Object.keys(error).some((key) => !Number.isNaN(Number(key)));
2457
+ const field = get(_fields, name);
2458
+ const hasNestedFields = isObject(field) && Object.keys(field).some((key) => key !== '_f');
2459
+ isFieldArrayRootError
2460
+ ? updateFieldArrayRootError(_formState.errors, { [name]: error }, name)
2461
+ : (error === null || error === void 0 ? void 0 : error.type) ||
2462
+ (error === null || error === void 0 ? void 0 : error.message) ||
2463
+ Array.isArray(error) ||
2464
+ (isObject(error) && hasNestedFields)
2465
+ ? set(_formState.errors, name, error)
2466
+ : unset(_formState.errors, name);
2460
2467
  }
2461
2468
  _formState.errors = { ..._formState.errors };
2462
2469
  }
2463
2470
  else {
2471
+ Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
2464
2472
  _formState.errors = errors;
2465
2473
  }
2466
2474
  return errors;
2467
2475
  };
2468
2476
  const validateForm = async ({ name, eventType, }) => {
2469
- if (props.validate) {
2470
- const result = await props.validate({
2477
+ if (_options.validate) {
2478
+ const resetCallId = _resetCallId;
2479
+ const result = await _options.validate({
2471
2480
  formValues: _formValues,
2472
2481
  formState: _formState,
2473
2482
  name,
2474
2483
  eventType,
2475
2484
  });
2485
+ if (resetCallId !== _resetCallId) {
2486
+ return true;
2487
+ }
2476
2488
  if (isObject(result)) {
2489
+ let isValid = true;
2490
+ clearErrors(FORM_ERROR_TYPE);
2477
2491
  for (const key in result) {
2478
2492
  const error = result[key];
2479
2493
  if (error) {
2494
+ isValid = false;
2480
2495
  setError(`${FORM_ERROR_TYPE}.${key}`, {
2481
2496
  message: isString(error.message) ? error.message : '',
2482
2497
  type: error.type || INPUT_VALIDATION_RULES.validate,
2483
2498
  });
2484
2499
  }
2485
2500
  }
2501
+ return isValid;
2486
2502
  }
2487
2503
  else if (isString(result) || !result) {
2488
2504
  setError(FORM_ERROR_TYPE, {
2489
2505
  message: result || '',
2490
2506
  type: INPUT_VALIDATION_RULES.validate,
2491
2507
  });
2508
+ return false;
2492
2509
  }
2493
2510
  else {
2494
2511
  clearErrors(FORM_ERROR_TYPE);
2512
+ return true;
2495
2513
  }
2496
- return result;
2497
2514
  }
2498
2515
  return true;
2499
2516
  };
@@ -2501,7 +2518,8 @@ function createFormControl(props = {}) {
2501
2518
  valid: true,
2502
2519
  runRootValidation: false,
2503
2520
  }, }) => {
2504
- if (props.validate) {
2521
+ const resetCallId = _resetCallId;
2522
+ if (_options.validate && !context.runRootValidation) {
2505
2523
  context.runRootValidation = true;
2506
2524
  const result = await validateForm({
2507
2525
  name,
@@ -2521,14 +2539,14 @@ function createFormControl(props = {}) {
2521
2539
  if (_f) {
2522
2540
  const isFieldArrayRoot = _names.array.has(_f.name);
2523
2541
  const isPromiseFunction = field._f && hasPromiseValidation(field._f);
2524
- const shouldTrackIsValidatingState = _proxyFormState.validatingFields ||
2525
- _proxyFormState.isValidating ||
2526
- _proxySubscribeFormState.validatingFields ||
2527
- _proxySubscribeFormState.isValidating;
2542
+ const shouldTrackIsValidatingState = _isTracked('isValidating', 'validatingFields');
2528
2543
  if (isPromiseFunction && shouldTrackIsValidatingState) {
2529
2544
  _updateIsValidating([_f.name], true);
2530
2545
  }
2531
2546
  const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !onlyCheckValid, isFieldArrayRoot);
2547
+ if (resetCallId !== _resetCallId) {
2548
+ return context.valid;
2549
+ }
2532
2550
  if (isPromiseFunction && shouldTrackIsValidatingState) {
2533
2551
  _updateIsValidating([_f.name]);
2534
2552
  }
@@ -2538,12 +2556,14 @@ function createFormControl(props = {}) {
2538
2556
  break;
2539
2557
  }
2540
2558
  }
2541
- !onlyCheckValid &&
2542
- (get(fieldError, _f.name)
2559
+ if (!onlyCheckValid) {
2560
+ cancelDelayedError(_f.name);
2561
+ get(fieldError, _f.name)
2543
2562
  ? isFieldArrayRoot
2544
2563
  ? updateFieldArrayRootError(_formState.errors, fieldError, _f.name)
2545
2564
  : set(_formState.errors, _f.name, fieldError[_f.name])
2546
- : unset(_formState.errors, _f.name));
2565
+ : unset(_formState.errors, _f.name);
2566
+ }
2547
2567
  if (props.shouldUseNativeValidation && fieldError[_f.name]) {
2548
2568
  break;
2549
2569
  }
@@ -2629,11 +2649,25 @@ function createFormControl(props = {}) {
2629
2649
  }
2630
2650
  }
2631
2651
  (options.shouldDirty || options.shouldTouch) &&
2632
- updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, !skipRender);
2652
+ updateTouchAndDirty(name, field &&
2653
+ field._f &&
2654
+ !field._f.disabled &&
2655
+ (field._f.valueAsNumber ||
2656
+ field._f.valueAsDate ||
2657
+ field._f.setValueAs)
2658
+ ? getFieldValueAs(value, field._f)
2659
+ : fieldValue, options.shouldTouch, options.shouldDirty, !skipRender);
2633
2660
  options.shouldValidate &&
2634
2661
  trigger(name, {
2635
2662
  delayError: options.delayError,
2636
2663
  });
2664
+ if (options.shouldValidate &&
2665
+ field &&
2666
+ field._f &&
2667
+ field._f.deps &&
2668
+ (!Array.isArray(field._f.deps) || field._f.deps.length > 0)) {
2669
+ trigger(field._f.deps);
2670
+ }
2637
2671
  };
2638
2672
  const setFieldValues = (name, value, options, skipClone = false, skipRender = false, skipValueRender = false) => {
2639
2673
  if (_names.array.has(name)) {
@@ -2671,11 +2705,7 @@ function createFormControl(props = {}) {
2671
2705
  name,
2672
2706
  values: skipClone ? _formValues : cloneObject(_formValues),
2673
2707
  });
2674
- if ((_proxyFormState.isDirty ||
2675
- _proxyFormState.dirtyFields ||
2676
- _proxySubscribeFormState.isDirty ||
2677
- _proxySubscribeFormState.dirtyFields) &&
2678
- options.shouldDirty) {
2708
+ if (_isTracked('isDirty', 'dirtyFields') && options.shouldDirty) {
2679
2709
  _updateDirtyFields();
2680
2710
  if (!skipStateEmit) {
2681
2711
  _subjects.state.next({
@@ -2685,6 +2715,10 @@ function createFormControl(props = {}) {
2685
2715
  });
2686
2716
  }
2687
2717
  }
2718
+ options.shouldValidate &&
2719
+ trigger(name, {
2720
+ delayError: options.delayError,
2721
+ });
2688
2722
  }
2689
2723
  else {
2690
2724
  const isEmpty = (Array.isArray(cloneValue) && !cloneValue.length) ||
@@ -2758,7 +2792,7 @@ function createFormControl(props = {}) {
2758
2792
  : getEventValue(event);
2759
2793
  const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
2760
2794
  const hasNoValidationEffect = !hasValidation(field._f) &&
2761
- !props.validate &&
2795
+ !_options.validate &&
2762
2796
  !_options.resolver &&
2763
2797
  !get(_formState.errors, name) &&
2764
2798
  !field._f.deps;
@@ -2788,7 +2822,7 @@ function createFormControl(props = {}) {
2788
2822
  });
2789
2823
  if (shouldSkipValidation) {
2790
2824
  if ((!hasNoValidationEffect || !_formState.isValid) &&
2791
- (_proxyFormState.isValid || _proxySubscribeFormState.isValid)) {
2825
+ _isTracked('isValid')) {
2792
2826
  if (_options.mode === 'onBlur') {
2793
2827
  if (isBlurEvent) {
2794
2828
  _setValid();
@@ -2801,7 +2835,7 @@ function createFormControl(props = {}) {
2801
2835
  return (shouldRender &&
2802
2836
  _subjects.state.next({ name, ...(watched ? {} : fieldState) }));
2803
2837
  }
2804
- if (!_options.resolver && props.validate) {
2838
+ if (!_options.resolver && _options.validate) {
2805
2839
  await validateForm({
2806
2840
  name: name,
2807
2841
  eventType: event.type,
@@ -2809,7 +2843,11 @@ function createFormControl(props = {}) {
2809
2843
  }
2810
2844
  !isBlurEvent && watched && _subjects.state.next({ ..._formState });
2811
2845
  if (_options.resolver) {
2846
+ const resetCallId = _resetCallId;
2812
2847
  const { errors } = await _runSchema([name]);
2848
+ if (resetCallId !== _resetCallId) {
2849
+ return;
2850
+ }
2813
2851
  _updateIsValidating([name]);
2814
2852
  _updateIsFieldValueUpdated(fieldValue);
2815
2853
  if (!isFieldValueUpdated) {
@@ -2823,22 +2861,28 @@ function createFormControl(props = {}) {
2823
2861
  isValid = isEmptyObject(errors);
2824
2862
  }
2825
2863
  else {
2864
+ const resetCallId = _resetCallId;
2826
2865
  _updateIsValidating([name], true);
2827
2866
  error = (await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation))[name];
2867
+ if (resetCallId !== _resetCallId) {
2868
+ return;
2869
+ }
2828
2870
  _updateIsValidating([name]);
2829
2871
  _updateIsFieldValueUpdated(fieldValue);
2830
2872
  if (isFieldValueUpdated) {
2831
2873
  if (error) {
2832
2874
  isValid = false;
2833
2875
  }
2834
- else if (_proxyFormState.isValid ||
2835
- _proxySubscribeFormState.isValid) {
2876
+ else if (_isTracked('isValid')) {
2836
2877
  isValid = await executeBuiltInValidation({
2837
2878
  fields: _fields,
2838
2879
  onlyCheckValid: true,
2839
2880
  name: name,
2840
2881
  eventType: event.type,
2841
2882
  });
2883
+ if (resetCallId !== _resetCallId) {
2884
+ return;
2885
+ }
2842
2886
  }
2843
2887
  }
2844
2888
  }
@@ -2862,11 +2906,15 @@ function createFormControl(props = {}) {
2862
2906
  let validationResult;
2863
2907
  const fieldNames = convertToArrayPayload(name);
2864
2908
  if (_options.resolver) {
2909
+ const resetCallId = _resetCallId;
2865
2910
  const errors = await executeSchemaAndUpdateState(isUndefined(name) ? name : fieldNames);
2866
2911
  isValid = isEmptyObject(errors);
2867
2912
  validationResult = name
2868
2913
  ? !fieldNames.some((name) => get(errors, name))
2869
2914
  : isValid;
2915
+ if (resetCallId !== _resetCallId) {
2916
+ return validationResult;
2917
+ }
2870
2918
  }
2871
2919
  else if (name) {
2872
2920
  validationResult = (await Promise.all(fieldNames.map(async (fieldName) => {
@@ -2893,8 +2941,7 @@ function createFormControl(props = {}) {
2893
2941
  delayErrorCallbacks[name](_options.delayError);
2894
2942
  }
2895
2943
  else {
2896
- clearTimeout(timers[name]);
2897
- delete delayErrorCallbacks[name];
2944
+ cancelDelayedError(name);
2898
2945
  }
2899
2946
  }
2900
2947
  if (options.shouldTouch) {
@@ -2905,13 +2952,11 @@ function createFormControl(props = {}) {
2905
2952
  }
2906
2953
  _subjects.state.next({
2907
2954
  ...(!isString(name) ||
2908
- ((_proxyFormState.isValid || _proxySubscribeFormState.isValid) &&
2909
- isValid !== _formState.isValid)
2955
+ (_isTracked('isValid') && isValid !== _formState.isValid)
2910
2956
  ? {}
2911
2957
  : { name }),
2912
2958
  ...(_options.resolver || !name ? { isValid } : {}),
2913
- ...(options.shouldTouch &&
2914
- (_proxyFormState.touchedFields || _proxySubscribeFormState.touchedFields)
2959
+ ...(options.shouldTouch && _isTracked('touchedFields')
2915
2960
  ? { touchedFields: _formState.touchedFields }
2916
2961
  : {}),
2917
2962
  errors: _formState.errors,
@@ -2946,15 +2991,16 @@ function createFormControl(props = {}) {
2946
2991
  invalid: !!error,
2947
2992
  isDirty: !!get(targetFormState.dirtyFields, name),
2948
2993
  error,
2949
- isValidating: !!get(_formState.validatingFields, name),
2994
+ isValidating: !!get(targetFormState.validatingFields, name),
2950
2995
  isTouched: !!get(targetFormState.touchedFields, name),
2951
2996
  };
2952
2997
  };
2953
2998
  const clearErrors = (name) => {
2954
2999
  const names = name ? convertToArrayPayload(name) : undefined;
2955
- names === null || names === void 0 ? void 0 : names.forEach((inputName) => unset(_formState.errors, inputName));
2956
3000
  if (names) {
2957
3001
  names.forEach((inputName) => {
3002
+ cancelDelayedErrorTree(inputName);
3003
+ unset(_formState.errors, inputName);
2958
3004
  _subjects.state.next({
2959
3005
  name: inputName,
2960
3006
  errors: _formState.errors,
@@ -2962,6 +3008,7 @@ function createFormControl(props = {}) {
2962
3008
  });
2963
3009
  }
2964
3010
  else {
3011
+ Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
2965
3012
  _formState.errors = {};
2966
3013
  _subjects.state.next({
2967
3014
  errors: _formState.errors,
@@ -2969,9 +3016,10 @@ function createFormControl(props = {}) {
2969
3016
  }
2970
3017
  };
2971
3018
  const setError = (name, error, options) => {
3019
+ cancelDelayedErrorTree(name);
2972
3020
  const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
2973
3021
  const currentError = get(_formState.errors, name) || {};
2974
- const { ref: currentRef, message, type, ...restOfErrorTree } = currentError;
3022
+ const { ref: currentRef, message, type, types, ...restOfErrorTree } = currentError;
2975
3023
  set(_formState.errors, name, {
2976
3024
  ...restOfErrorTree,
2977
3025
  ...error,
@@ -3056,11 +3104,15 @@ function createFormControl(props = {}) {
3056
3104
  for (const fieldName of name ? convertToArrayPayload(name) : _names.mount) {
3057
3105
  _names.mount.delete(fieldName);
3058
3106
  _names.array.delete(fieldName);
3107
+ _names.disabled.delete(fieldName);
3059
3108
  if (!options.keepValue) {
3060
3109
  unset(_fields, fieldName);
3061
3110
  unset(_formValues, fieldName);
3062
3111
  }
3063
- !options.keepError && unset(_formState.errors, fieldName);
3112
+ if (!options.keepError) {
3113
+ cancelDelayedErrorTree(fieldName);
3114
+ unset(_formState.errors, fieldName);
3115
+ }
3064
3116
  !options.keepDirty && unset(_formState.dirtyFields, fieldName);
3065
3117
  !options.keepTouched && unset(_formState.touchedFields, fieldName);
3066
3118
  !options.keepIsValidating &&
@@ -3076,6 +3128,9 @@ function createFormControl(props = {}) {
3076
3128
  _subjects.state.next({
3077
3129
  ..._formState,
3078
3130
  ...(options.keepDirty ? {} : { isDirty: _getDirty() }),
3131
+ ...(options.keepIsValidating
3132
+ ? {}
3133
+ : { isValidating: !isEmptyObject(_formState.validatingFields) }),
3079
3134
  });
3080
3135
  !options.keepIsValid && _setValid();
3081
3136
  };
@@ -3104,6 +3159,14 @@ function createFormControl(props = {}) {
3104
3159
  },
3105
3160
  });
3106
3161
  _names.mount.add(name);
3162
+ if (field && field._f) {
3163
+ const nextField = get(_fields, name);
3164
+ for (const rule of REGISTER_VALIDATION_RULES) {
3165
+ if (!(rule in options)) {
3166
+ delete nextField._f[rule];
3167
+ }
3168
+ }
3169
+ }
3107
3170
  if (field && !shouldRevalidateRemount) {
3108
3171
  _setDisabledField({
3109
3172
  disabled: isBoolean(options.disabled)
@@ -3198,7 +3261,7 @@ function createFormControl(props = {}) {
3198
3261
  });
3199
3262
  }
3200
3263
  }
3201
- }, 0, false);
3264
+ }, 0);
3202
3265
  }
3203
3266
  };
3204
3267
  const handleSubmit = (onValid, onInvalid) => async (e) => {
@@ -3214,23 +3277,32 @@ function createFormControl(props = {}) {
3214
3277
  isSubmitting: true,
3215
3278
  });
3216
3279
  if (_options.resolver) {
3280
+ const resetCallId = _resetCallId;
3217
3281
  const { errors, values } = await _runSchema();
3282
+ if (resetCallId !== _resetCallId) {
3283
+ return;
3284
+ }
3218
3285
  _updateIsValidating();
3286
+ Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
3219
3287
  _formState.errors = errors;
3220
3288
  fieldValues = cloneObject(values);
3221
3289
  }
3222
3290
  else {
3291
+ const resetCallId = _resetCallId;
3223
3292
  await executeBuiltInValidation({
3224
3293
  fields: _fields,
3225
3294
  eventType: EVENTS.SUBMIT,
3226
3295
  });
3296
+ if (resetCallId !== _resetCallId) {
3297
+ return;
3298
+ }
3299
+ unset(_formState.errors, ROOT_ERROR_TYPE);
3227
3300
  }
3228
3301
  if (_names.disabled.size) {
3229
3302
  for (const name of _names.disabled) {
3230
3303
  unset(fieldValues, name);
3231
3304
  }
3232
3305
  }
3233
- unset(_formState.errors, ROOT_ERROR_TYPE);
3234
3306
  if (isEmptyObject(_formState.errors)) {
3235
3307
  _subjects.state.next({
3236
3308
  errors: {},
@@ -3263,6 +3335,7 @@ function createFormControl(props = {}) {
3263
3335
  };
3264
3336
  const resetField = (name, options = {}) => {
3265
3337
  if (get(_fields, name)) {
3338
+ unset(_formState.validatingFields, name);
3266
3339
  if (isUndefined(options.defaultValue)) {
3267
3340
  setValue(name, cloneObject(get(_defaultValues, name)));
3268
3341
  }
@@ -3280,18 +3353,24 @@ function createFormControl(props = {}) {
3280
3353
  : _getDirty();
3281
3354
  }
3282
3355
  if (!options.keepError) {
3356
+ cancelDelayedErrorTree(name);
3283
3357
  unset(_formState.errors, name);
3284
3358
  _setValid();
3285
3359
  }
3286
- _subjects.state.next({ ..._formState });
3360
+ _subjects.state.next({
3361
+ ..._formState,
3362
+ isValidating: !isEmptyObject(_formState.validatingFields),
3363
+ });
3287
3364
  }
3288
3365
  };
3289
3366
  const _reset = (formValues, keepStateOptions = {}) => {
3367
+ _resetCallId++;
3290
3368
  const updatedValues = formValues ? cloneObject(formValues) : _defaultValues;
3291
3369
  const cloneUpdatedValues = cloneObject(updatedValues);
3292
3370
  const isEmptyResetValues = isEmptyObject(formValues);
3293
3371
  const values = cloneUpdatedValues;
3294
3372
  const fieldRefs = _fields;
3373
+ Object.keys(delayErrorCallbacks).forEach(cancelDelayedError);
3295
3374
  if (!keepStateOptions.keepDefaultValues) {
3296
3375
  _defaultValues = updatedValues;
3297
3376
  }
@@ -3409,10 +3488,16 @@ function createFormControl(props = {}) {
3409
3488
  ? getDirtyFields(_defaultValues, formValues, undefined, fieldRefs)
3410
3489
  : keepStateOptions.keepDirty
3411
3490
  ? _formState.dirtyFields
3412
- : {},
3491
+ : keepStateOptions.keepValues
3492
+ ? getDirtyFields(_defaultValues, _formValues, undefined, fieldRefs)
3493
+ : {},
3413
3494
  touchedFields: keepStateOptions.keepTouched
3414
3495
  ? _formState.touchedFields
3415
3496
  : {},
3497
+ ...(!keepStateOptions.keepIsValidating &&
3498
+ (_formState.isValidating || !isEmptyObject(_formState.validatingFields))
3499
+ ? { validatingFields: {}, isValidating: false }
3500
+ : null),
3416
3501
  errors: keepStateOptions.keepErrors ? _formState.errors : {},
3417
3502
  isSubmitSuccessful: keepStateOptions.keepIsSubmitSuccessful
3418
3503
  ? _formState.isSubmitSuccessful
@@ -3534,6 +3619,8 @@ function createFormControl(props = {}) {
3534
3619
  };
3535
3620
  _validationModeBeforeSubmit = getValidationModes(_options.mode);
3536
3621
  _validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
3622
+ shouldDisplayAllAssociatedErrors =
3623
+ _options.criteriaMode === VALIDATION_MODE.all;
3537
3624
  },
3538
3625
  },
3539
3626
  subscribe,
@@ -3561,38 +3648,24 @@ function createFormControl(props = {}) {
3561
3648
  }
3562
3649
 
3563
3650
  /**
3564
- * Custom hook to manage the entire form.
3565
- *
3566
- * @remarks
3567
- * [API](https://react-hook-form.com/docs/useform) • [Demo](https://codesandbox.io/s/react-hook-form-get-started-ts-5ksmm) • [Video](https://www.youtube.com/watch?v=RkXv4AXXC_4)
3568
- *
3569
- * @param props - form configuration and validation parameters.
3651
+ * Core hook for managing a form. Returns all methods and state for
3652
+ * registration, validation, and submission.
3570
3653
  *
3571
- * @returns methods - individual functions to manage the form state. {@link UseFormReturn}
3654
+ * @see [API](https://react-hook-form.com/docs/useform)
3572
3655
  *
3573
3656
  * @example
3574
3657
  * ```tsx
3575
- * function App() {
3576
- * const { register, handleSubmit, watch, formState: { errors } } = useForm();
3577
- * const onSubmit = data => console.log(data);
3578
- *
3579
- * console.log(watch("example"));
3580
- *
3581
- * return (
3582
- * <form onSubmit={handleSubmit(onSubmit)}>
3583
- * <input defaultValue="test" {...register("example")} />
3584
- * <input {...register("exampleRequired", { required: true })} />
3585
- * {errors.exampleRequired && <span>This field is required</span>}
3586
- * <button>Submit</button>
3587
- * </form>
3588
- * );
3589
- * }
3658
+ * const { register, handleSubmit, formState: { errors } } = useForm<FormValues>();
3659
+ * <form onSubmit={handleSubmit(onSubmit)}>
3660
+ * <input {...register("email", { required: true })} />
3661
+ * </form>
3590
3662
  * ```
3591
3663
  */
3592
3664
  function useForm(props = {}) {
3593
3665
  const _formControl = React.useRef(undefined);
3594
3666
  const _values = React.useRef(undefined);
3595
3667
  const _formControlProp = React.useRef(props.formControl);
3668
+ const _hadValidate = React.useRef(!!props.validate);
3596
3669
  const [formState, updateFormState] = React.useState(() => ({
3597
3670
  ...cloneObject(DEFAULT_FORM_STATE),
3598
3671
  isLoading: isFunction(props.defaultValues),
@@ -3623,13 +3696,13 @@ function useForm(props = {}) {
3623
3696
  }
3624
3697
  }
3625
3698
  const control = _formControl.current.control;
3626
- control._options = props;
3627
- const { resyncIfNeeded, snapshot } = useResyncOnReconnect();
3699
+ control._options = { ...props, validate: props.validate };
3700
+ const getCurrentFormState = () => ({
3701
+ ...control._formState,
3702
+ defaultValues: control._defaultValues,
3703
+ });
3704
+ const { resyncIfNeeded, snapshot } = useResyncOnReconnect(getCurrentFormState);
3628
3705
  useIsomorphicLayoutEffect(() => {
3629
- const getCurrentFormState = () => ({
3630
- ...control._formState,
3631
- defaultValues: control._defaultValues,
3632
- });
3633
3706
  resyncIfNeeded(true, getCurrentFormState, updateFormState);
3634
3707
  const unsubscribe = control._subscribe({
3635
3708
  formState: control._proxyFormState,
@@ -3664,6 +3737,13 @@ function useForm(props = {}) {
3664
3737
  control._focusError();
3665
3738
  }
3666
3739
  }, [control, props.errors]);
3740
+ React.useEffect(() => {
3741
+ var _a;
3742
+ if (_hadValidate.current && !props.validate) {
3743
+ (_a = _formControl.current) === null || _a === void 0 ? void 0 : _a.clearErrors(FORM_ERROR_TYPE);
3744
+ }
3745
+ _hadValidate.current = !!props.validate;
3746
+ }, [props.validate]);
3667
3747
  React.useEffect(() => {
3668
3748
  props.shouldUnregister &&
3669
3749
  control._subjects.state.next({
@@ -3713,32 +3793,20 @@ function useForm(props = {}) {
3713
3793
  }
3714
3794
 
3715
3795
  /**
3716
- * Watch component that subscribes to form field changes and re-renders when watched fields update.
3796
+ * Component wrapper around `useWatch`. Re-renders only when watched fields change.
3717
3797
  *
3718
- * @param control - The form control object from useForm
3719
- * @param name - Can be field name, array of field names, or undefined to watch the entire form
3720
- * @param disabled - Disable subscription
3721
- * @param exact - Whether to watch exact field names or not
3722
- * @param defaultValue - The default value to use if the field is not yet set
3723
- * @param compute - Function to compute derived values from watched fields
3724
- * @param render - The function that receives watched values and returns ReactNode
3725
- * @returns The result of calling render function with watched values
3798
+ * @see [API](https://react-hook-form.com/docs/usewatch)
3726
3799
  *
3727
3800
  * @example
3728
- * The `Watch` component only re-render when the values of `foo`, `bar`, and `baz.qux` change.
3729
- * The types of `foo`, `bar`, and `baz.qux` are precisely inferred.
3730
- *
3731
3801
  * ```tsx
3732
- * const { control } = useForm();
3733
- *
3734
3802
  * <Watch
3735
3803
  * control={control}
3736
- * names={['foo', 'bar', 'baz.qux']}
3737
- * render={([foo, bar, baz_qux]) => <div>{foo}{bar}{baz_qux}</div>}
3804
+ * names={["foo", "bar"]}
3805
+ * render={([foo, bar]) => <span>{foo} {bar}</span>}
3738
3806
  * />
3739
3807
  * ```
3740
3808
  */
3741
3809
  const Watch = (props) => props.render(useWatch({ name: props.names, ...props }));
3742
3810
 
3743
- export { Controller, FieldArray, Form, FormProvider, FormState, FormStateSubscribe, Watch, appendErrors, createFormControl, get, set, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch };
3811
+ export { Controller, ErrorMessage, FieldArray, Form, FormProvider, FormState, FormStateSubscribe, Watch, appendErrors, createFormControl, get, set, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch };
3744
3812
  //# sourceMappingURL=index.esm.mjs.map