@conform-to/dom 1.0.2 → 1.0.4

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.
package/README CHANGED
@@ -8,7 +8,7 @@
8
8
  ╚══════╝ ╚═════╝ ╚═╝ ╚══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
9
9
 
10
10
 
11
- Version 1.0.2 / License MIT / Copyright (c) 2024 Edmund Hung
11
+ Version 1.0.3 / License MIT / Copyright (c) 2024 Edmund Hung
12
12
 
13
13
  A type-safe form validation library utilizing web fundamentals to progressively enhance HTML Forms with full support for server frameworks like Remix and Next.js.
14
14
 
package/form.d.ts CHANGED
@@ -5,11 +5,11 @@ export type Combine<T> = {
5
5
  [K in keyof BaseCombine<T>]: BaseCombine<T>[K];
6
6
  };
7
7
  export type DefaultValue<Schema> = Schema extends string | number | boolean | Date | bigint | null | undefined ? Schema | string | null | undefined : Schema extends File ? null | undefined : Schema extends Array<infer Item> ? Array<DefaultValue<Item>> | null | undefined : Schema extends Record<string, any> ? {
8
- [Key in keyof Combine<Schema>]?: DefaultValue<Combine<Schema>[Key]>;
8
+ [Key in keyof Schema]?: DefaultValue<Schema[Key]>;
9
9
  } | null | undefined : string | null | undefined;
10
10
  export type FormValue<Schema> = Schema extends string | number | boolean | Date | bigint | null | undefined ? string | undefined : Schema extends File ? File | undefined : Schema extends File[] ? File | Array<File> | undefined : Schema extends Array<infer Item> ? string | Array<FormValue<Item>> | undefined : Schema extends Record<string, any> ? {
11
- [Key in keyof Combine<Schema>]?: DefaultValue<Combine<Schema>[Key]>;
12
- } | undefined : unknown;
11
+ [Key in keyof Schema]?: FormValue<Schema[Key]>;
12
+ } | null | undefined : unknown;
13
13
  declare const error: unique symbol;
14
14
  declare const field: unique symbol;
15
15
  declare const form: unique symbol;
package/form.js CHANGED
@@ -11,9 +11,7 @@ var submission = require('./submission.js');
11
11
  function createFormMeta(options, initialized) {
12
12
  var _lastResult$initialVa, _options$constraint, _lastResult$state$val, _lastResult$state, _ref;
13
13
  var lastResult = !initialized ? options.lastResult : undefined;
14
- var defaultValue = options.defaultValue ?
15
- // @ts-expect-error
16
- submission.serialize(options.defaultValue) : {};
14
+ var defaultValue = options.defaultValue ? submission.serialize(options.defaultValue) : {};
17
15
  var initialValue = (_lastResult$initialVa = lastResult === null || lastResult === void 0 ? void 0 : lastResult.initialValue) !== null && _lastResult$initialVa !== void 0 ? _lastResult$initialVa : defaultValue;
18
16
  var result = {
19
17
  submissionStatus: lastResult === null || lastResult === void 0 ? void 0 : lastResult.status,
@@ -29,9 +27,7 @@ function createFormMeta(options, initialized) {
29
27
  // We can consider adding a warning if it happens
30
28
  error: (_ref = lastResult === null || lastResult === void 0 ? void 0 : lastResult.error) !== null && _ref !== void 0 ? _ref : {}
31
29
  };
32
- if (lastResult !== null && lastResult !== void 0 && lastResult.intent) {
33
- handleIntent(result, lastResult.intent);
34
- }
30
+ handleIntent(result, lastResult === null || lastResult === void 0 ? void 0 : lastResult.intent, lastResult === null || lastResult === void 0 ? void 0 : lastResult.fields);
35
31
  return result;
36
32
  }
37
33
  function getDefaultKey(defaultValue, prefix) {
@@ -47,24 +43,71 @@ function getDefaultKey(defaultValue, prefix) {
47
43
  return result;
48
44
  }, {});
49
45
  }
50
- function handleIntent(meta, intent, initialized) {
46
+ function setFieldsValidated(meta, fields) {
47
+ for (var _name of Object.keys(meta.error).concat(fields !== null && fields !== void 0 ? fields : [])) {
48
+ meta.validated[_name] = true;
49
+ }
50
+ }
51
+ function handleIntent(meta, intent, fields, initialized) {
52
+ if (!intent) {
53
+ setFieldsValidated(meta, fields);
54
+ return;
55
+ }
51
56
  switch (intent.type) {
57
+ case 'validate':
58
+ {
59
+ if (intent.payload.name) {
60
+ meta.validated[intent.payload.name] = true;
61
+ } else {
62
+ setFieldsValidated(meta, fields);
63
+ }
64
+ break;
65
+ }
52
66
  case 'update':
53
67
  {
54
- if (typeof intent.payload.value !== 'undefined') {
55
- var _intent$payload$name;
56
- var _name = (_intent$payload$name = intent.payload.name) !== null && _intent$payload$name !== void 0 ? _intent$payload$name : '';
57
- var value = submission.serialize(intent.payload.value);
58
- updateValue(meta, _name, value);
68
+ var {
69
+ name: _name2,
70
+ validated,
71
+ value
72
+ } = intent.payload;
73
+ if (typeof value !== 'undefined') {
74
+ updateValue(meta, _name2 !== null && _name2 !== void 0 ? _name2 : '', submission.serialize(value));
75
+ }
76
+ if (typeof validated !== 'undefined') {
77
+ // Clean up previous validated state
78
+ if (_name2) {
79
+ submission.setState(meta.validated, _name2, () => undefined);
80
+ } else {
81
+ meta.validated = {};
82
+ }
83
+ if (validated) {
84
+ if (formdata.isPlainObject(value) || Array.isArray(value)) {
85
+ Object.assign(meta.validated, formdata.flatten(value, {
86
+ resolve() {
87
+ return true;
88
+ },
89
+ prefix: _name2
90
+ }));
91
+ }
92
+ meta.validated[_name2 !== null && _name2 !== void 0 ? _name2 : ''] = true;
93
+ } else if (_name2) {
94
+ delete meta.validated[_name2];
95
+ }
59
96
  }
60
97
  break;
61
98
  }
62
99
  case 'reset':
63
100
  {
64
- var _intent$payload$name2;
65
- var _name2 = (_intent$payload$name2 = intent.payload.name) !== null && _intent$payload$name2 !== void 0 ? _intent$payload$name2 : '';
66
- var _value = formdata.getValue(meta.defaultValue, _name2);
67
- updateValue(meta, _name2, _value);
101
+ var _intent$payload$name;
102
+ var _name3 = (_intent$payload$name = intent.payload.name) !== null && _intent$payload$name !== void 0 ? _intent$payload$name : '';
103
+ var _value = formdata.getValue(meta.defaultValue, _name3);
104
+ updateValue(meta, _name3, _value);
105
+ if (_name3) {
106
+ submission.setState(meta.validated, _name3, () => undefined);
107
+ delete meta.validated[_name3];
108
+ } else {
109
+ meta.validated = {};
110
+ }
68
111
  break;
69
112
  }
70
113
  case 'insert':
@@ -77,9 +120,18 @@ function handleIntent(meta, intent, initialized) {
77
120
  submission.setListState(meta.key, intent, util.generateId);
78
121
  submission.setListValue(meta.initialValue, intent);
79
122
  }
123
+ submission.setListState(meta.validated, intent);
124
+ meta.validated[intent.payload.name] = true;
80
125
  break;
81
126
  }
82
127
  }
128
+ meta.error = Object.entries(meta.error).reduce((result, _ref3) => {
129
+ var [name, error] = _ref3;
130
+ if (meta.validated[name]) {
131
+ result[name] = error;
132
+ }
133
+ return result;
134
+ }, {});
83
135
  }
84
136
  function updateValue(meta, name, value) {
85
137
  meta.initialValue = util.clone(meta.initialValue);
@@ -170,8 +222,8 @@ function createValidProxy(error) {
170
222
  function createDirtyProxy(defaultValue, value, shouldDirtyConsider) {
171
223
  return createStateProxy(name => JSON.stringify(defaultValue[name]) !== JSON.stringify(value[name], (key, value) => {
172
224
  if (name === '' && key === '' && value) {
173
- return Object.entries(value).reduce((result, _ref3) => {
174
- var [name, value] = _ref3;
225
+ return Object.entries(value).reduce((result, _ref4) => {
226
+ var [name, value] = _ref4;
175
227
  if (!shouldDirtyConsider(name)) {
176
228
  return result;
177
229
  }
@@ -190,11 +242,11 @@ function shouldNotify(prev, next, cache, scope) {
190
242
  var prefixes = (_scope$prefix = scope.prefix) !== null && _scope$prefix !== void 0 ? _scope$prefix : [];
191
243
  var names = (_scope$name = scope.name) !== null && _scope$name !== void 0 ? _scope$name : [];
192
244
  var list = prefixes.length === 0 ? names : Array.from(new Set([...Object.keys(prev), ...Object.keys(next)]));
193
- var _loop = function _loop(_name3) {
194
- if (prefixes.length === 0 || names.includes(_name3) || prefixes.some(prefix => formdata.isPrefix(_name3, prefix))) {
245
+ var _loop = function _loop(_name4) {
246
+ if (prefixes.length === 0 || names.includes(_name4) || prefixes.some(prefix => formdata.isPrefix(_name4, prefix))) {
195
247
  var _cache$_name;
196
- (_cache$_name = cache[_name3]) !== null && _cache$_name !== void 0 ? _cache$_name : cache[_name3] = compareFn(prev[_name3], next[_name3]);
197
- if (cache[_name3]) {
248
+ (_cache$_name = cache[_name4]) !== null && _cache$_name !== void 0 ? _cache$_name : cache[_name4] = compareFn(prev[_name4], next[_name4]);
249
+ if (cache[_name4]) {
198
250
  return {
199
251
  v: true
200
252
  };
@@ -202,8 +254,8 @@ function shouldNotify(prev, next, cache, scope) {
202
254
  }
203
255
  },
204
256
  _ret;
205
- for (var _name3 of list) {
206
- _ret = _loop(_name3);
257
+ for (var _name4 of list) {
258
+ _ret = _loop(_name4);
207
259
  if (_ret) return _ret.v;
208
260
  }
209
261
  }
@@ -268,19 +320,6 @@ function createFormContext(options) {
268
320
  var next = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
269
321
  return prev !== next;
270
322
  }
271
- function getStateInput(form) {
272
- var element = form.elements.namedItem(submission.STATE);
273
- util.invariant(element === null || dom.isFieldElement(element), "The input name \"".concat(submission.STATE, "\" is reserved by Conform. Please use another name."));
274
- if (!element) {
275
- var input = document.createElement('input');
276
- input.type = 'hidden';
277
- input.name = submission.STATE;
278
- input.value = '';
279
- form.append(input);
280
- return input;
281
- }
282
- return element;
283
- }
284
323
  function getSerializedState() {
285
324
  return JSON.stringify({
286
325
  validated: meta.validated
@@ -290,10 +329,6 @@ function createFormContext(options) {
290
329
  var form = event.target;
291
330
  var submitter = event.submitter;
292
331
  util.invariant(form === getFormElement(), "The submit event is dispatched by form#".concat(form.id, " instead of form#").concat(latestOptions.formId));
293
- var input = getStateInput(form);
294
-
295
- // To ensure it capturing latest state before parsing
296
- input.value = getSerializedState();
297
332
  var formData = formdata.getFormData(form, submitter);
298
333
  var result = {
299
334
  formData,
@@ -372,14 +407,14 @@ function createFormContext(options) {
372
407
  updateFormMeta(createFormMeta(latestOptions, true));
373
408
  }
374
409
  function report(result) {
375
- var _result$error, _result$state$validat, _result$state;
410
+ var _result$error, _result$state;
376
411
  var formElement = getFormElement();
377
412
  if (!result.initialValue) {
378
413
  formElement === null || formElement === void 0 || formElement.reset();
379
414
  return;
380
415
  }
381
- var error = Object.entries((_result$error = result.error) !== null && _result$error !== void 0 ? _result$error : {}).reduce((result, _ref4) => {
382
- var [name, newError] = _ref4;
416
+ var error = Object.entries((_result$error = result.error) !== null && _result$error !== void 0 ? _result$error : {}).reduce((result, _ref5) => {
417
+ var [name, newError] = _ref5;
383
418
  var error = newError === null ? meta.error[name] : newError;
384
419
  if (error) {
385
420
  result[name] = error;
@@ -389,16 +424,14 @@ function createFormContext(options) {
389
424
  var update = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, meta), {}, {
390
425
  submissionStatus: result.status,
391
426
  value: result.initialValue,
392
- error,
393
- validated: (_result$state$validat = (_result$state = result.state) === null || _result$state === void 0 ? void 0 : _result$state.validated) !== null && _result$state$validat !== void 0 ? _result$state$validat : {}
427
+ validated: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, meta.validated), (_result$state = result.state) === null || _result$state === void 0 ? void 0 : _result$state.validated),
428
+ error
394
429
  });
395
- if (result.intent) {
396
- handleIntent(update, result.intent, true);
397
- }
430
+ handleIntent(update, result.intent, result.fields, true);
398
431
  updateFormMeta(update);
399
432
  if (formElement && result.status === 'error') {
400
433
  for (var element of formElement.elements) {
401
- if (dom.isFieldElement(element) && error[element.name]) {
434
+ if (dom.isFieldElement(element) && meta.error[element.name]) {
402
435
  element.focus();
403
436
  break;
404
437
  }
package/form.mjs CHANGED
@@ -1,15 +1,13 @@
1
1
  import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.mjs';
2
- import { flatten, formatPaths, getPaths, getValue, setValue, isPlainObject, normalize, getFormData, isPrefix } from './formdata.mjs';
2
+ import { flatten, formatPaths, getPaths, getValue, isPlainObject, setValue, normalize, getFormData, isPrefix } from './formdata.mjs';
3
3
  import { getFormAction, getFormEncType, getFormMethod, isFieldElement, requestSubmit } from './dom.mjs';
4
4
  import { generateId, clone, invariant } from './util.mjs';
5
- import { serialize, setListState, setListValue, setState, getSubmissionContext, INTENT, serializeIntent, STATE } from './submission.mjs';
5
+ import { serialize, setListState, setListValue, setState, getSubmissionContext, INTENT, serializeIntent } from './submission.mjs';
6
6
 
7
7
  function createFormMeta(options, initialized) {
8
8
  var _lastResult$initialVa, _options$constraint, _lastResult$state$val, _lastResult$state, _ref;
9
9
  var lastResult = !initialized ? options.lastResult : undefined;
10
- var defaultValue = options.defaultValue ?
11
- // @ts-expect-error
12
- serialize(options.defaultValue) : {};
10
+ var defaultValue = options.defaultValue ? serialize(options.defaultValue) : {};
13
11
  var initialValue = (_lastResult$initialVa = lastResult === null || lastResult === void 0 ? void 0 : lastResult.initialValue) !== null && _lastResult$initialVa !== void 0 ? _lastResult$initialVa : defaultValue;
14
12
  var result = {
15
13
  submissionStatus: lastResult === null || lastResult === void 0 ? void 0 : lastResult.status,
@@ -25,9 +23,7 @@ function createFormMeta(options, initialized) {
25
23
  // We can consider adding a warning if it happens
26
24
  error: (_ref = lastResult === null || lastResult === void 0 ? void 0 : lastResult.error) !== null && _ref !== void 0 ? _ref : {}
27
25
  };
28
- if (lastResult !== null && lastResult !== void 0 && lastResult.intent) {
29
- handleIntent(result, lastResult.intent);
30
- }
26
+ handleIntent(result, lastResult === null || lastResult === void 0 ? void 0 : lastResult.intent, lastResult === null || lastResult === void 0 ? void 0 : lastResult.fields);
31
27
  return result;
32
28
  }
33
29
  function getDefaultKey(defaultValue, prefix) {
@@ -43,24 +39,71 @@ function getDefaultKey(defaultValue, prefix) {
43
39
  return result;
44
40
  }, {});
45
41
  }
46
- function handleIntent(meta, intent, initialized) {
42
+ function setFieldsValidated(meta, fields) {
43
+ for (var _name of Object.keys(meta.error).concat(fields !== null && fields !== void 0 ? fields : [])) {
44
+ meta.validated[_name] = true;
45
+ }
46
+ }
47
+ function handleIntent(meta, intent, fields, initialized) {
48
+ if (!intent) {
49
+ setFieldsValidated(meta, fields);
50
+ return;
51
+ }
47
52
  switch (intent.type) {
53
+ case 'validate':
54
+ {
55
+ if (intent.payload.name) {
56
+ meta.validated[intent.payload.name] = true;
57
+ } else {
58
+ setFieldsValidated(meta, fields);
59
+ }
60
+ break;
61
+ }
48
62
  case 'update':
49
63
  {
50
- if (typeof intent.payload.value !== 'undefined') {
51
- var _intent$payload$name;
52
- var _name = (_intent$payload$name = intent.payload.name) !== null && _intent$payload$name !== void 0 ? _intent$payload$name : '';
53
- var value = serialize(intent.payload.value);
54
- updateValue(meta, _name, value);
64
+ var {
65
+ name: _name2,
66
+ validated,
67
+ value
68
+ } = intent.payload;
69
+ if (typeof value !== 'undefined') {
70
+ updateValue(meta, _name2 !== null && _name2 !== void 0 ? _name2 : '', serialize(value));
71
+ }
72
+ if (typeof validated !== 'undefined') {
73
+ // Clean up previous validated state
74
+ if (_name2) {
75
+ setState(meta.validated, _name2, () => undefined);
76
+ } else {
77
+ meta.validated = {};
78
+ }
79
+ if (validated) {
80
+ if (isPlainObject(value) || Array.isArray(value)) {
81
+ Object.assign(meta.validated, flatten(value, {
82
+ resolve() {
83
+ return true;
84
+ },
85
+ prefix: _name2
86
+ }));
87
+ }
88
+ meta.validated[_name2 !== null && _name2 !== void 0 ? _name2 : ''] = true;
89
+ } else if (_name2) {
90
+ delete meta.validated[_name2];
91
+ }
55
92
  }
56
93
  break;
57
94
  }
58
95
  case 'reset':
59
96
  {
60
- var _intent$payload$name2;
61
- var _name2 = (_intent$payload$name2 = intent.payload.name) !== null && _intent$payload$name2 !== void 0 ? _intent$payload$name2 : '';
62
- var _value = getValue(meta.defaultValue, _name2);
63
- updateValue(meta, _name2, _value);
97
+ var _intent$payload$name;
98
+ var _name3 = (_intent$payload$name = intent.payload.name) !== null && _intent$payload$name !== void 0 ? _intent$payload$name : '';
99
+ var _value = getValue(meta.defaultValue, _name3);
100
+ updateValue(meta, _name3, _value);
101
+ if (_name3) {
102
+ setState(meta.validated, _name3, () => undefined);
103
+ delete meta.validated[_name3];
104
+ } else {
105
+ meta.validated = {};
106
+ }
64
107
  break;
65
108
  }
66
109
  case 'insert':
@@ -73,9 +116,18 @@ function handleIntent(meta, intent, initialized) {
73
116
  setListState(meta.key, intent, generateId);
74
117
  setListValue(meta.initialValue, intent);
75
118
  }
119
+ setListState(meta.validated, intent);
120
+ meta.validated[intent.payload.name] = true;
76
121
  break;
77
122
  }
78
123
  }
124
+ meta.error = Object.entries(meta.error).reduce((result, _ref3) => {
125
+ var [name, error] = _ref3;
126
+ if (meta.validated[name]) {
127
+ result[name] = error;
128
+ }
129
+ return result;
130
+ }, {});
79
131
  }
80
132
  function updateValue(meta, name, value) {
81
133
  meta.initialValue = clone(meta.initialValue);
@@ -166,8 +218,8 @@ function createValidProxy(error) {
166
218
  function createDirtyProxy(defaultValue, value, shouldDirtyConsider) {
167
219
  return createStateProxy(name => JSON.stringify(defaultValue[name]) !== JSON.stringify(value[name], (key, value) => {
168
220
  if (name === '' && key === '' && value) {
169
- return Object.entries(value).reduce((result, _ref3) => {
170
- var [name, value] = _ref3;
221
+ return Object.entries(value).reduce((result, _ref4) => {
222
+ var [name, value] = _ref4;
171
223
  if (!shouldDirtyConsider(name)) {
172
224
  return result;
173
225
  }
@@ -186,11 +238,11 @@ function shouldNotify(prev, next, cache, scope) {
186
238
  var prefixes = (_scope$prefix = scope.prefix) !== null && _scope$prefix !== void 0 ? _scope$prefix : [];
187
239
  var names = (_scope$name = scope.name) !== null && _scope$name !== void 0 ? _scope$name : [];
188
240
  var list = prefixes.length === 0 ? names : Array.from(new Set([...Object.keys(prev), ...Object.keys(next)]));
189
- var _loop = function _loop(_name3) {
190
- if (prefixes.length === 0 || names.includes(_name3) || prefixes.some(prefix => isPrefix(_name3, prefix))) {
241
+ var _loop = function _loop(_name4) {
242
+ if (prefixes.length === 0 || names.includes(_name4) || prefixes.some(prefix => isPrefix(_name4, prefix))) {
191
243
  var _cache$_name;
192
- (_cache$_name = cache[_name3]) !== null && _cache$_name !== void 0 ? _cache$_name : cache[_name3] = compareFn(prev[_name3], next[_name3]);
193
- if (cache[_name3]) {
244
+ (_cache$_name = cache[_name4]) !== null && _cache$_name !== void 0 ? _cache$_name : cache[_name4] = compareFn(prev[_name4], next[_name4]);
245
+ if (cache[_name4]) {
194
246
  return {
195
247
  v: true
196
248
  };
@@ -198,8 +250,8 @@ function shouldNotify(prev, next, cache, scope) {
198
250
  }
199
251
  },
200
252
  _ret;
201
- for (var _name3 of list) {
202
- _ret = _loop(_name3);
253
+ for (var _name4 of list) {
254
+ _ret = _loop(_name4);
203
255
  if (_ret) return _ret.v;
204
256
  }
205
257
  }
@@ -264,19 +316,6 @@ function createFormContext(options) {
264
316
  var next = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
265
317
  return prev !== next;
266
318
  }
267
- function getStateInput(form) {
268
- var element = form.elements.namedItem(STATE);
269
- invariant(element === null || isFieldElement(element), "The input name \"".concat(STATE, "\" is reserved by Conform. Please use another name."));
270
- if (!element) {
271
- var input = document.createElement('input');
272
- input.type = 'hidden';
273
- input.name = STATE;
274
- input.value = '';
275
- form.append(input);
276
- return input;
277
- }
278
- return element;
279
- }
280
319
  function getSerializedState() {
281
320
  return JSON.stringify({
282
321
  validated: meta.validated
@@ -286,10 +325,6 @@ function createFormContext(options) {
286
325
  var form = event.target;
287
326
  var submitter = event.submitter;
288
327
  invariant(form === getFormElement(), "The submit event is dispatched by form#".concat(form.id, " instead of form#").concat(latestOptions.formId));
289
- var input = getStateInput(form);
290
-
291
- // To ensure it capturing latest state before parsing
292
- input.value = getSerializedState();
293
328
  var formData = getFormData(form, submitter);
294
329
  var result = {
295
330
  formData,
@@ -368,14 +403,14 @@ function createFormContext(options) {
368
403
  updateFormMeta(createFormMeta(latestOptions, true));
369
404
  }
370
405
  function report(result) {
371
- var _result$error, _result$state$validat, _result$state;
406
+ var _result$error, _result$state;
372
407
  var formElement = getFormElement();
373
408
  if (!result.initialValue) {
374
409
  formElement === null || formElement === void 0 || formElement.reset();
375
410
  return;
376
411
  }
377
- var error = Object.entries((_result$error = result.error) !== null && _result$error !== void 0 ? _result$error : {}).reduce((result, _ref4) => {
378
- var [name, newError] = _ref4;
412
+ var error = Object.entries((_result$error = result.error) !== null && _result$error !== void 0 ? _result$error : {}).reduce((result, _ref5) => {
413
+ var [name, newError] = _ref5;
379
414
  var error = newError === null ? meta.error[name] : newError;
380
415
  if (error) {
381
416
  result[name] = error;
@@ -385,16 +420,14 @@ function createFormContext(options) {
385
420
  var update = _objectSpread2(_objectSpread2({}, meta), {}, {
386
421
  submissionStatus: result.status,
387
422
  value: result.initialValue,
388
- error,
389
- validated: (_result$state$validat = (_result$state = result.state) === null || _result$state === void 0 ? void 0 : _result$state.validated) !== null && _result$state$validat !== void 0 ? _result$state$validat : {}
423
+ validated: _objectSpread2(_objectSpread2({}, meta.validated), (_result$state = result.state) === null || _result$state === void 0 ? void 0 : _result$state.validated),
424
+ error
390
425
  });
391
- if (result.intent) {
392
- handleIntent(update, result.intent, true);
393
- }
426
+ handleIntent(update, result.intent, result.fields, true);
394
427
  updateFormMeta(update);
395
428
  if (formElement && result.status === 'error') {
396
429
  for (var element of formElement.elements) {
397
- if (isFieldElement(element) && error[element.name]) {
430
+ if (isFieldElement(element) && meta.error[element.name]) {
398
431
  element.focus();
399
432
  break;
400
433
  }
package/formdata.d.ts CHANGED
@@ -45,9 +45,9 @@ export declare function isFile(obj: unknown): obj is File;
45
45
  /**
46
46
  * Normalize value by removing empty object or array, empty string and null values
47
47
  */
48
- export declare function normalize<Type extends Record<string, unknown>>(value: Type | null): Type | null | undefined;
49
- export declare function normalize<Type extends Array<unknown>>(value: Type | null): Type | null | undefined;
50
- export declare function normalize(value: unknown): unknown | undefined;
48
+ export declare function normalize<Type extends Record<string, unknown>>(value: Type, acceptFile?: boolean): Type | undefined;
49
+ export declare function normalize<Type extends Array<unknown>>(value: Type, acceptFile?: boolean): Type | undefined;
50
+ export declare function normalize(value: unknown, acceptFile?: boolean): unknown | undefined;
51
51
  /**
52
52
  * Flatten a tree into a dictionary
53
53
  */
package/formdata.js CHANGED
@@ -136,9 +136,10 @@ function isFile(obj) {
136
136
  */
137
137
 
138
138
  function normalize(value) {
139
+ var acceptFile = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
139
140
  if (isPlainObject(value)) {
140
141
  var obj = Object.keys(value).sort().reduce((result, key) => {
141
- var data = normalize(value[key]);
142
+ var data = normalize(value[key], acceptFile);
142
143
  if (typeof data !== 'undefined') {
143
144
  result[key] = data;
144
145
  }
@@ -153,20 +154,11 @@ function normalize(value) {
153
154
  if (value.length === 0) {
154
155
  return undefined;
155
156
  }
156
- return value.map(normalize);
157
+ return value.map(item => normalize(item, acceptFile));
157
158
  }
158
- if (typeof value === 'string' && value === '' || value === null || isFile(value) && value.size === 0) {
159
+ if (typeof value === 'string' && value === '' || value === null || isFile(value) && (!acceptFile || value.size === 0)) {
159
160
  return;
160
161
  }
161
-
162
- // We will skip serializing file if the result is sent to the client
163
- if (isFile(value)) {
164
- return Object.assign(value, {
165
- toJSON() {
166
- return;
167
- }
168
- });
169
- }
170
162
  return value;
171
163
  }
172
164
 
package/formdata.mjs CHANGED
@@ -132,9 +132,10 @@ function isFile(obj) {
132
132
  */
133
133
 
134
134
  function normalize(value) {
135
+ var acceptFile = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
135
136
  if (isPlainObject(value)) {
136
137
  var obj = Object.keys(value).sort().reduce((result, key) => {
137
- var data = normalize(value[key]);
138
+ var data = normalize(value[key], acceptFile);
138
139
  if (typeof data !== 'undefined') {
139
140
  result[key] = data;
140
141
  }
@@ -149,20 +150,11 @@ function normalize(value) {
149
150
  if (value.length === 0) {
150
151
  return undefined;
151
152
  }
152
- return value.map(normalize);
153
+ return value.map(item => normalize(item, acceptFile));
153
154
  }
154
- if (typeof value === 'string' && value === '' || value === null || isFile(value) && value.size === 0) {
155
+ if (typeof value === 'string' && value === '' || value === null || isFile(value) && (!acceptFile || value.size === 0)) {
155
156
  return;
156
157
  }
157
-
158
- // We will skip serializing file if the result is sent to the client
159
- if (isFile(value)) {
160
- return Object.assign(value, {
161
- toJSON() {
162
- return;
163
- }
164
- });
165
- }
166
158
  return value;
167
159
  }
168
160
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "A set of opinionated helpers built on top of the Constraint Validation API",
4
4
  "homepage": "https://conform.guide",
5
5
  "license": "MIT",
6
- "version": "1.0.2",
6
+ "version": "1.0.4",
7
7
  "main": "index.js",
8
8
  "module": "index.mjs",
9
9
  "types": "index.d.ts",
package/submission.d.ts CHANGED
@@ -5,10 +5,10 @@ export type SubmissionState = {
5
5
  export type SubmissionContext<Value = null, FormError = string[]> = {
6
6
  intent: Intent | null;
7
7
  payload: Record<string, unknown>;
8
- fields: string[];
8
+ fields: Set<string>;
9
9
  value?: Value;
10
10
  error?: Record<string, FormError | null> | null;
11
- state: SubmissionState;
11
+ state?: SubmissionState;
12
12
  };
13
13
  export type Submission<Schema, FormError = string[], FormValue = Schema> = {
14
14
  status: 'success';
@@ -25,6 +25,7 @@ export type SubmissionResult<FormError = string[]> = {
25
25
  status?: 'error' | 'success';
26
26
  intent?: Intent;
27
27
  initialValue?: Record<string, unknown> | null;
28
+ fields?: string[];
28
29
  error?: Record<string, FormError | null>;
29
30
  state?: SubmissionState;
30
31
  };
package/submission.js CHANGED
@@ -18,15 +18,21 @@ var STATE = '__state__';
18
18
  function getSubmissionContext(body) {
19
19
  var intent = body.get(INTENT);
20
20
  var state = body.get(STATE);
21
- var payload = {};
22
- var fields = [];
23
21
  util.invariant((typeof intent === 'string' || intent === null) && (typeof state === 'string' || state === null), "The input name \"".concat(INTENT, "\" and \"").concat(STATE, "\" are reserved by Conform. Please use another name for your input."));
22
+ var context = {
23
+ payload: {},
24
+ fields: new Set(),
25
+ intent: getIntent(intent)
26
+ };
27
+ if (state) {
28
+ context.state = JSON.parse(state);
29
+ }
24
30
  var _loop = function _loop(next) {
25
31
  if (name === INTENT || name === STATE) {
26
32
  return 1; // continue
27
33
  }
28
- fields.push(name);
29
- formdata.setValue(payload, name, prev => {
34
+ context.fields.add(name);
35
+ formdata.setValue(context.payload, name, prev => {
30
36
  if (!prev) {
31
37
  return next;
32
38
  } else if (Array.isArray(prev)) {
@@ -39,30 +45,17 @@ function getSubmissionContext(body) {
39
45
  for (var [name, next] of body.entries()) {
40
46
  if (_loop(next)) continue;
41
47
  }
42
- return {
43
- payload,
44
- intent: getIntent(intent),
45
- state: state ? JSON.parse(state) : {
46
- validated: {}
47
- },
48
- fields
49
- };
48
+ return context;
50
49
  }
51
50
  function parse(payload, options) {
52
51
  var context = getSubmissionContext(payload);
53
52
  var intent = context.intent;
54
53
  if (intent) {
55
54
  switch (intent.type) {
56
- case 'validate':
57
- if (intent.payload.name) {
58
- context.state.validated[intent.payload.name] = true;
59
- }
60
- break;
61
55
  case 'update':
62
56
  {
63
57
  var {
64
- name,
65
- validated
58
+ name
66
59
  } = intent.payload;
67
60
  var _value = serialize(intent.payload.value);
68
61
  if (typeof _value !== 'undefined') {
@@ -73,27 +66,6 @@ function parse(payload, options) {
73
66
  context.payload = _value;
74
67
  }
75
68
  }
76
- if (typeof validated !== 'undefined') {
77
- // Clean up previous validated state
78
- if (name) {
79
- setState(context.state.validated, name, () => undefined);
80
- } else {
81
- context.state.validated = {};
82
- }
83
- if (validated) {
84
- if (formdata.isPlainObject(_value) || Array.isArray(_value)) {
85
- Object.assign(context.state.validated, formdata.flatten(_value, {
86
- resolve() {
87
- return true;
88
- },
89
- prefix: name
90
- }));
91
- }
92
- context.state.validated[name !== null && name !== void 0 ? name : ''] = true;
93
- } else if (name) {
94
- delete context.state.validated[name];
95
- }
96
- }
97
69
  break;
98
70
  }
99
71
  case 'reset':
@@ -103,11 +75,8 @@ function parse(payload, options) {
103
75
  } = intent.payload;
104
76
  if (_name) {
105
77
  formdata.setValue(context.payload, _name, () => undefined);
106
- setState(context.state.validated, _name, () => undefined);
107
- delete context.state.validated[_name];
108
78
  } else {
109
79
  context.payload = {};
110
- context.state.validated = {};
111
80
  }
112
81
  break;
113
82
  }
@@ -116,25 +85,15 @@ function parse(payload, options) {
116
85
  case 'reorder':
117
86
  {
118
87
  setListValue(context.payload, intent);
119
- setListState(context.state.validated, intent);
120
- context.state.validated[intent.payload.name] = true;
121
88
  break;
122
89
  }
123
90
  }
124
91
  }
125
92
  var result = options.resolve(context.payload, intent);
126
- var mergeResolveResult = resolved => {
127
- var error = typeof resolved.error !== 'undefined' ? resolved.error : {};
128
- if (!intent || intent.type === 'validate' && !intent.payload.name) {
129
- for (var _name2 of [...context.fields, ...Object.keys(error !== null && error !== void 0 ? error : {})]) {
130
- context.state.validated[_name2] = true;
131
- }
132
- }
133
- return createSubmission(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, context), {}, {
134
- value: resolved.value,
135
- error: resolved.error
136
- }));
137
- };
93
+ var mergeResolveResult = resolved => createSubmission(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, context), {}, {
94
+ value: resolved.value,
95
+ error: resolved.error
96
+ }));
138
97
  if (result instanceof Promise) {
139
98
  return result.then(mergeResolveResult);
140
99
  }
@@ -161,50 +120,34 @@ function createSubmission(context) {
161
120
  };
162
121
  }
163
122
  function replySubmission(context) {
164
- var _context$intent, _options$formErrors, _normalize;
123
+ var _context$intent, _context$intent$paylo, _options$formErrors, _normalize;
165
124
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
166
- switch ((_context$intent = context.intent) === null || _context$intent === void 0 ? void 0 : _context$intent.type) {
167
- case 'reset':
168
- {
169
- var _context$intent$paylo;
170
- var name = (_context$intent$paylo = context.intent.payload.name) !== null && _context$intent$paylo !== void 0 ? _context$intent$paylo : '';
171
- if (name === '') {
172
- return {
173
- initialValue: null
174
- };
175
- }
176
- }
177
- }
178
- if ('resetForm' in options && options.resetForm) {
125
+ if ('resetForm' in options && options.resetForm || ((_context$intent = context.intent) === null || _context$intent === void 0 ? void 0 : _context$intent.type) === 'reset' && ((_context$intent$paylo = context.intent.payload.name) !== null && _context$intent$paylo !== void 0 ? _context$intent$paylo : '') === '') {
179
126
  return {
180
127
  initialValue: null
181
128
  };
182
129
  }
183
130
  if ('hideFields' in options && options.hideFields) {
184
- for (var _name3 of options.hideFields) {
185
- var _value2 = formdata.getValue(context.payload, _name3);
131
+ for (var name of options.hideFields) {
132
+ var _value2 = formdata.getValue(context.payload, name);
186
133
  if (typeof _value2 !== 'undefined') {
187
- formdata.setValue(context.payload, _name3, () => undefined);
134
+ formdata.setValue(context.payload, name, () => undefined);
188
135
  }
189
136
  }
190
137
  }
191
- var submissionError = context.error ? Object.entries(context.error).reduce((result, _ref) => {
192
- var [name, error] = _ref;
193
- if (context.state.validated[name]) {
194
- result[name] = error;
195
- }
196
- return result;
197
- }, {}) : undefined;
198
138
  var extraError = 'formErrors' in options || 'fieldErrors' in options ? formdata.normalize(_rollupPluginBabelHelpers.objectSpread2({
199
139
  '': (_options$formErrors = options.formErrors) !== null && _options$formErrors !== void 0 ? _options$formErrors : null
200
140
  }, options.fieldErrors)) : null;
201
- var error = submissionError || extraError ? _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, submissionError), extraError) : undefined;
141
+ var error = context.error || extraError ? _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, context.error), extraError) : undefined;
202
142
  return {
203
143
  status: context.intent ? undefined : error ? 'error' : 'success',
204
144
  intent: context.intent ? context.intent : undefined,
205
- initialValue: (_normalize = formdata.normalize(context.payload)) !== null && _normalize !== void 0 ? _normalize : {},
145
+ initialValue: (_normalize = formdata.normalize(context.payload,
146
+ // We can't serialize the file and send it back from the server, but we can preserve it in the client
147
+ typeof document !== 'undefined')) !== null && _normalize !== void 0 ? _normalize : {},
206
148
  error,
207
- state: context.state
149
+ state: context.state,
150
+ fields: Array.from(context.fields)
208
151
  };
209
152
  }
210
153
  function getIntent(serializedIntent) {
@@ -310,8 +253,8 @@ function setListState(state, intent, getDefaultValue) {
310
253
  function serialize(defaultValue) {
311
254
  if (formdata.isPlainObject(defaultValue)) {
312
255
  // @ts-expect-error FIXME
313
- return Object.entries(defaultValue).reduce((result, _ref2) => {
314
- var [key, value] = _ref2;
256
+ return Object.entries(defaultValue).reduce((result, _ref) => {
257
+ var [key, value] = _ref;
315
258
  // @ts-ignore-error FIXME
316
259
  result[key] = serialize(value);
317
260
  return result;
package/submission.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.mjs';
2
- import { setValue, isPlainObject, flatten, getValue, normalize, isPrefix } from './formdata.mjs';
2
+ import { setValue, isPlainObject, getValue, normalize, flatten, isPrefix } from './formdata.mjs';
3
3
  import { invariant } from './util.mjs';
4
4
 
5
5
  /**
@@ -14,15 +14,21 @@ var STATE = '__state__';
14
14
  function getSubmissionContext(body) {
15
15
  var intent = body.get(INTENT);
16
16
  var state = body.get(STATE);
17
- var payload = {};
18
- var fields = [];
19
17
  invariant((typeof intent === 'string' || intent === null) && (typeof state === 'string' || state === null), "The input name \"".concat(INTENT, "\" and \"").concat(STATE, "\" are reserved by Conform. Please use another name for your input."));
18
+ var context = {
19
+ payload: {},
20
+ fields: new Set(),
21
+ intent: getIntent(intent)
22
+ };
23
+ if (state) {
24
+ context.state = JSON.parse(state);
25
+ }
20
26
  var _loop = function _loop(next) {
21
27
  if (name === INTENT || name === STATE) {
22
28
  return 1; // continue
23
29
  }
24
- fields.push(name);
25
- setValue(payload, name, prev => {
30
+ context.fields.add(name);
31
+ setValue(context.payload, name, prev => {
26
32
  if (!prev) {
27
33
  return next;
28
34
  } else if (Array.isArray(prev)) {
@@ -35,30 +41,17 @@ function getSubmissionContext(body) {
35
41
  for (var [name, next] of body.entries()) {
36
42
  if (_loop(next)) continue;
37
43
  }
38
- return {
39
- payload,
40
- intent: getIntent(intent),
41
- state: state ? JSON.parse(state) : {
42
- validated: {}
43
- },
44
- fields
45
- };
44
+ return context;
46
45
  }
47
46
  function parse(payload, options) {
48
47
  var context = getSubmissionContext(payload);
49
48
  var intent = context.intent;
50
49
  if (intent) {
51
50
  switch (intent.type) {
52
- case 'validate':
53
- if (intent.payload.name) {
54
- context.state.validated[intent.payload.name] = true;
55
- }
56
- break;
57
51
  case 'update':
58
52
  {
59
53
  var {
60
- name,
61
- validated
54
+ name
62
55
  } = intent.payload;
63
56
  var _value = serialize(intent.payload.value);
64
57
  if (typeof _value !== 'undefined') {
@@ -69,27 +62,6 @@ function parse(payload, options) {
69
62
  context.payload = _value;
70
63
  }
71
64
  }
72
- if (typeof validated !== 'undefined') {
73
- // Clean up previous validated state
74
- if (name) {
75
- setState(context.state.validated, name, () => undefined);
76
- } else {
77
- context.state.validated = {};
78
- }
79
- if (validated) {
80
- if (isPlainObject(_value) || Array.isArray(_value)) {
81
- Object.assign(context.state.validated, flatten(_value, {
82
- resolve() {
83
- return true;
84
- },
85
- prefix: name
86
- }));
87
- }
88
- context.state.validated[name !== null && name !== void 0 ? name : ''] = true;
89
- } else if (name) {
90
- delete context.state.validated[name];
91
- }
92
- }
93
65
  break;
94
66
  }
95
67
  case 'reset':
@@ -99,11 +71,8 @@ function parse(payload, options) {
99
71
  } = intent.payload;
100
72
  if (_name) {
101
73
  setValue(context.payload, _name, () => undefined);
102
- setState(context.state.validated, _name, () => undefined);
103
- delete context.state.validated[_name];
104
74
  } else {
105
75
  context.payload = {};
106
- context.state.validated = {};
107
76
  }
108
77
  break;
109
78
  }
@@ -112,25 +81,15 @@ function parse(payload, options) {
112
81
  case 'reorder':
113
82
  {
114
83
  setListValue(context.payload, intent);
115
- setListState(context.state.validated, intent);
116
- context.state.validated[intent.payload.name] = true;
117
84
  break;
118
85
  }
119
86
  }
120
87
  }
121
88
  var result = options.resolve(context.payload, intent);
122
- var mergeResolveResult = resolved => {
123
- var error = typeof resolved.error !== 'undefined' ? resolved.error : {};
124
- if (!intent || intent.type === 'validate' && !intent.payload.name) {
125
- for (var _name2 of [...context.fields, ...Object.keys(error !== null && error !== void 0 ? error : {})]) {
126
- context.state.validated[_name2] = true;
127
- }
128
- }
129
- return createSubmission(_objectSpread2(_objectSpread2({}, context), {}, {
130
- value: resolved.value,
131
- error: resolved.error
132
- }));
133
- };
89
+ var mergeResolveResult = resolved => createSubmission(_objectSpread2(_objectSpread2({}, context), {}, {
90
+ value: resolved.value,
91
+ error: resolved.error
92
+ }));
134
93
  if (result instanceof Promise) {
135
94
  return result.then(mergeResolveResult);
136
95
  }
@@ -157,50 +116,34 @@ function createSubmission(context) {
157
116
  };
158
117
  }
159
118
  function replySubmission(context) {
160
- var _context$intent, _options$formErrors, _normalize;
119
+ var _context$intent, _context$intent$paylo, _options$formErrors, _normalize;
161
120
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
162
- switch ((_context$intent = context.intent) === null || _context$intent === void 0 ? void 0 : _context$intent.type) {
163
- case 'reset':
164
- {
165
- var _context$intent$paylo;
166
- var name = (_context$intent$paylo = context.intent.payload.name) !== null && _context$intent$paylo !== void 0 ? _context$intent$paylo : '';
167
- if (name === '') {
168
- return {
169
- initialValue: null
170
- };
171
- }
172
- }
173
- }
174
- if ('resetForm' in options && options.resetForm) {
121
+ if ('resetForm' in options && options.resetForm || ((_context$intent = context.intent) === null || _context$intent === void 0 ? void 0 : _context$intent.type) === 'reset' && ((_context$intent$paylo = context.intent.payload.name) !== null && _context$intent$paylo !== void 0 ? _context$intent$paylo : '') === '') {
175
122
  return {
176
123
  initialValue: null
177
124
  };
178
125
  }
179
126
  if ('hideFields' in options && options.hideFields) {
180
- for (var _name3 of options.hideFields) {
181
- var _value2 = getValue(context.payload, _name3);
127
+ for (var name of options.hideFields) {
128
+ var _value2 = getValue(context.payload, name);
182
129
  if (typeof _value2 !== 'undefined') {
183
- setValue(context.payload, _name3, () => undefined);
130
+ setValue(context.payload, name, () => undefined);
184
131
  }
185
132
  }
186
133
  }
187
- var submissionError = context.error ? Object.entries(context.error).reduce((result, _ref) => {
188
- var [name, error] = _ref;
189
- if (context.state.validated[name]) {
190
- result[name] = error;
191
- }
192
- return result;
193
- }, {}) : undefined;
194
134
  var extraError = 'formErrors' in options || 'fieldErrors' in options ? normalize(_objectSpread2({
195
135
  '': (_options$formErrors = options.formErrors) !== null && _options$formErrors !== void 0 ? _options$formErrors : null
196
136
  }, options.fieldErrors)) : null;
197
- var error = submissionError || extraError ? _objectSpread2(_objectSpread2({}, submissionError), extraError) : undefined;
137
+ var error = context.error || extraError ? _objectSpread2(_objectSpread2({}, context.error), extraError) : undefined;
198
138
  return {
199
139
  status: context.intent ? undefined : error ? 'error' : 'success',
200
140
  intent: context.intent ? context.intent : undefined,
201
- initialValue: (_normalize = normalize(context.payload)) !== null && _normalize !== void 0 ? _normalize : {},
141
+ initialValue: (_normalize = normalize(context.payload,
142
+ // We can't serialize the file and send it back from the server, but we can preserve it in the client
143
+ typeof document !== 'undefined')) !== null && _normalize !== void 0 ? _normalize : {},
202
144
  error,
203
- state: context.state
145
+ state: context.state,
146
+ fields: Array.from(context.fields)
204
147
  };
205
148
  }
206
149
  function getIntent(serializedIntent) {
@@ -306,8 +249,8 @@ function setListState(state, intent, getDefaultValue) {
306
249
  function serialize(defaultValue) {
307
250
  if (isPlainObject(defaultValue)) {
308
251
  // @ts-expect-error FIXME
309
- return Object.entries(defaultValue).reduce((result, _ref2) => {
310
- var [key, value] = _ref2;
252
+ return Object.entries(defaultValue).reduce((result, _ref) => {
253
+ var [key, value] = _ref;
311
254
  // @ts-ignore-error FIXME
312
255
  result[key] = serialize(value);
313
256
  return result;