@conform-to/dom 0.5.0 → 0.6.0-pre.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.
@@ -24,6 +24,7 @@ function _objectSpread2(target) {
24
24
  return target;
25
25
  }
26
26
  function _defineProperty(obj, key, value) {
27
+ key = _toPropertyKey(key);
27
28
  if (key in obj) {
28
29
  Object.defineProperty(obj, key, {
29
30
  value: value,
@@ -36,6 +37,22 @@ function _defineProperty(obj, key, value) {
36
37
  }
37
38
  return obj;
38
39
  }
40
+ function _toPrimitive(input, hint) {
41
+ if (typeof input !== "object" || input === null) return input;
42
+ var prim = input[Symbol.toPrimitive];
43
+ if (prim !== undefined) {
44
+ var res = prim.call(input, hint || "default");
45
+ if (typeof res !== "object") return res;
46
+ throw new TypeError("@@toPrimitive must return a primitive value.");
47
+ }
48
+ return (hint === "string" ? String : Number)(input);
49
+ }
50
+ function _toPropertyKey(arg) {
51
+ var key = _toPrimitive(arg, "string");
52
+ return typeof key === "symbol" ? key : String(key);
53
+ }
39
54
 
40
55
  exports.defineProperty = _defineProperty;
41
56
  exports.objectSpread2 = _objectSpread2;
57
+ exports.toPrimitive = _toPrimitive;
58
+ exports.toPropertyKey = _toPropertyKey;
package/index.d.ts CHANGED
@@ -1,37 +1,42 @@
1
- export declare type Primitive = null | undefined | string | number | boolean | Date;
2
- export declare type FieldElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement;
1
+ export type Primitive = null | undefined | string | number | boolean | Date;
2
+ export type FieldElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement;
3
3
  export interface FieldConfig<Schema = unknown> extends FieldConstraint<Schema> {
4
4
  id?: string;
5
5
  name: string;
6
6
  defaultValue?: FieldValue<Schema>;
7
- initialError?: Array<[string, string]>;
7
+ initialError?: Record<string, string | string[]>;
8
8
  form?: string;
9
9
  errorId?: string;
10
10
  }
11
- export declare type FieldValue<Schema> = Schema extends Primitive ? string : Schema extends File ? File : Schema extends Array<infer InnerType> ? Array<FieldValue<InnerType>> : Schema extends Record<string, any> ? {
11
+ export type FieldValue<Schema> = Schema extends Primitive ? string : Schema extends File ? File : Schema extends Array<infer InnerType> ? Array<FieldValue<InnerType>> : Schema extends Record<string, any> ? {
12
12
  [Key in keyof Schema]?: FieldValue<Schema[Key]>;
13
- } : unknown;
14
- export declare type FieldConstraint<Schema = any> = {
13
+ } : any;
14
+ export type FieldConstraint<Schema = any> = {
15
15
  required?: boolean;
16
16
  minLength?: number;
17
17
  maxLength?: number;
18
- min?: Schema extends number ? number : string;
19
- max?: Schema extends number ? number : string;
20
- step?: Schema extends number ? number : string;
18
+ min?: Schema extends number ? number : string | number;
19
+ max?: Schema extends number ? number : string | number;
20
+ step?: Schema extends number ? number : string | number;
21
21
  multiple?: boolean;
22
22
  pattern?: string;
23
23
  };
24
- export declare type FieldsetConstraint<Schema extends Record<string, any>> = {
24
+ export type FieldsetConstraint<Schema extends Record<string, any>> = {
25
25
  [Key in keyof Schema]?: FieldConstraint<Schema[Key]>;
26
26
  };
27
- export declare type Submission<Schema = unknown> = {
28
- type: string;
29
- intent?: string;
30
- value: FieldValue<Schema>;
31
- error: Array<[string, string]>;
27
+ export type Submission<Schema extends Record<string, any> | unknown = unknown> = unknown extends Schema ? {
28
+ intent: string;
29
+ payload: Record<string, any>;
30
+ error: Record<string, string | string[]>;
31
+ } : {
32
+ intent: string;
33
+ payload: Record<string, any>;
34
+ value?: Schema;
35
+ error: Record<string, string | string[]>;
36
+ toJSON(): Submission;
32
37
  };
33
- export interface CommandButtonProps<Name extends string = string> {
34
- name: `conform/${Name}`;
38
+ export interface IntentButtonProps {
39
+ name: '__intent__';
35
40
  value: string;
36
41
  formNoValidate?: boolean;
37
42
  }
@@ -39,9 +44,19 @@ export declare function isFieldElement(element: unknown): element is FieldElemen
39
44
  export declare function getFormElements(form: HTMLFormElement): FieldElement[];
40
45
  export declare function getPaths(name: string): Array<string | number>;
41
46
  export declare function getFormData(form: HTMLFormElement, submitter?: HTMLInputElement | HTMLButtonElement | null): FormData;
47
+ export type FormMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';
48
+ export type FormEncType = 'application/x-www-form-urlencoded' | 'multipart/form-data';
49
+ export declare function getFormAttributes(form: HTMLFormElement, submitter?: HTMLInputElement | HTMLButtonElement | null): {
50
+ action: string;
51
+ encType: FormEncType;
52
+ method: FormMethod;
53
+ };
42
54
  export declare function getName(paths: Array<string | number>): string;
43
- export declare function shouldValidate(submission: Submission, name: string): boolean;
44
- export declare function hasError(error: Array<[string, string]>, name?: string): boolean;
55
+ export declare function shouldValidate(intent: string, name: string): boolean;
56
+ export declare function getValidationMessage(errors?: string | string[]): string;
57
+ export declare function getErrors(message: string | undefined): string[];
58
+ export declare const VALIDATION_UNDEFINED = "__undefined__";
59
+ export declare const VALIDATION_SKIPPED = "__skipped__";
45
60
  export declare function reportSubmission(form: HTMLFormElement, submission: Submission): void;
46
61
  export declare function setValue<T>(target: any, paths: Array<string | number>, valueFn: (prev?: T) => T): void;
47
62
  /**
@@ -51,20 +66,47 @@ export declare function setValue<T>(target: any, paths: Array<string | number>,
51
66
  */
52
67
  export declare function requestSubmit(form: HTMLFormElement, submitter?: HTMLButtonElement | HTMLInputElement): void;
53
68
  /**
54
- * Creates a command button on demand and trigger a form submit by clicking it.
69
+ * Creates an intent button on demand and trigger a form submit by clicking it.
55
70
  */
56
- export declare function requestCommand(form: HTMLFormElement | undefined, buttonProps: CommandButtonProps): void;
71
+ export declare function requestIntent(form: HTMLFormElement | undefined, buttonProps: {
72
+ value: string;
73
+ formNoValidate?: boolean;
74
+ }): void;
57
75
  /**
58
76
  * Returns the properties required to configure a command button for validation
59
77
  *
60
78
  * @see https://conform.guide/api/react#validate
61
79
  */
62
- export declare function validate(field?: string): CommandButtonProps<'validate'>;
80
+ export declare function validate(field?: string): IntentButtonProps;
63
81
  export declare function getFormElement(element: HTMLFormElement | HTMLFieldSetElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement | null): HTMLFormElement | null;
64
82
  export declare function focus(field: FieldElement): void;
65
- export declare function getSubmissionType(name: string): string | null;
66
- export declare function parse<Schema extends Record<string, any>>(payload: FormData | URLSearchParams): Submission<Schema>;
67
- export declare type ListCommand<Schema = unknown> = {
83
+ export declare function parse(payload: FormData | URLSearchParams): Submission;
84
+ export declare function parse<Schema>(payload: FormData | URLSearchParams, options?: {
85
+ resolve?: (payload: Record<string, any>, intent: string) => {
86
+ value: Schema;
87
+ } | {
88
+ error: Record<string, string | string[]>;
89
+ };
90
+ }): Submission<Schema>;
91
+ export declare function parse<Schema>(payload: FormData | URLSearchParams, options?: {
92
+ resolve?: (payload: Record<string, any>, intent: string) => Promise<{
93
+ value: Schema;
94
+ } | {
95
+ error: Record<string, string | string[]>;
96
+ }>;
97
+ }): Promise<Submission<Schema>>;
98
+ export declare function parse<Schema>(payload: FormData | URLSearchParams, options?: {
99
+ resolve?: (payload: Record<string, any>, intent: string) => ({
100
+ value: Schema;
101
+ } | {
102
+ error: Record<string, string | string[]>;
103
+ }) | Promise<{
104
+ value: Schema;
105
+ } | {
106
+ error: Record<string, string | string[]>;
107
+ }>;
108
+ }): Submission<Schema> | Promise<Submission<Schema>>;
109
+ export type ListCommand<Schema = unknown> = {
68
110
  type: 'prepend';
69
111
  scope: string;
70
112
  payload: {
@@ -97,27 +139,26 @@ export declare type ListCommand<Schema = unknown> = {
97
139
  to: number;
98
140
  };
99
141
  };
100
- export declare function parseListCommand<Schema = unknown>(data: string): ListCommand<Schema>;
142
+ export declare function parseListCommand<Schema = unknown>(intent: string): ListCommand<Schema> | null;
101
143
  export declare function updateList<Schema>(list: Array<Schema>, command: ListCommand<Schema>): Array<Schema>;
102
- export declare function handleList<Schema>(submission: Submission<Schema>): Submission<Schema>;
103
144
  export interface ListCommandButtonBuilder {
104
145
  append<Schema>(name: string, payload?: {
105
146
  defaultValue: Schema;
106
- }): CommandButtonProps<'list'>;
147
+ }): IntentButtonProps;
107
148
  prepend<Schema>(name: string, payload?: {
108
149
  defaultValue: Schema;
109
- }): CommandButtonProps<'list'>;
150
+ }): IntentButtonProps;
110
151
  replace<Schema>(name: string, payload: {
111
152
  defaultValue: Schema;
112
153
  index: number;
113
- }): CommandButtonProps<'list'>;
154
+ }): IntentButtonProps;
114
155
  remove(name: string, payload: {
115
156
  index: number;
116
- }): CommandButtonProps<'list'>;
157
+ }): IntentButtonProps;
117
158
  reorder(name: string, payload: {
118
159
  from: number;
119
160
  to: number;
120
- }): CommandButtonProps<'list'>;
161
+ }): IntentButtonProps;
121
162
  }
122
163
  /**
123
164
  * Helpers to configure a command button for modifying a list
@@ -125,3 +166,26 @@ export interface ListCommandButtonBuilder {
125
166
  * @see https://conform.guide/api/react#list
126
167
  */
127
168
  export declare const list: ListCommandButtonBuilder;
169
+ /**
170
+ * Validate the form with the Constraint Validation API
171
+ * @see https://conform.guide/api/react#validateconstraint
172
+ */
173
+ export declare function validateConstraint(options: {
174
+ form: HTMLFormElement;
175
+ formData?: FormData;
176
+ constraint?: Record<Lowercase<string>, (value: string, context: {
177
+ formData: FormData;
178
+ attributeValue: string;
179
+ }) => boolean>;
180
+ acceptMultipleErrors?: ({ name, intent, payload, }: {
181
+ name: string;
182
+ intent: string;
183
+ payload: Record<string, any>;
184
+ }) => boolean;
185
+ formatMessages?: ({ name, validity, constraint, defaultErrors, }: {
186
+ name: string;
187
+ validity: ValidityState;
188
+ constraint: Record<string, boolean>;
189
+ defaultErrors: string[];
190
+ }) => string[];
191
+ }): Submission;
package/index.js CHANGED
@@ -4,6 +4,21 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
6
6
 
7
+ // type Join<K, P> = P extends string | number ?
8
+ // K extends string | number ?
9
+ // `${K}${"" extends P ? "" : "."}${P}`
10
+ // : never : never;
11
+
12
+ // type DottedPaths<T> = T extends object ?
13
+ // { [K in keyof T]-?: K extends string | number ?
14
+ // `${K}` | Join<K, DottedPaths<T[K]>>
15
+ // : never
16
+ // }[keyof T] : ""
17
+
18
+ // type Pathfix<T> = T extends `${infer Prefix}.${number}${infer Postfix}` ? `${Prefix}[${number}]${Pathfix<Postfix>}` : T;
19
+
20
+ // type Path<Schema> = Pathfix<DottedPaths<Schema>> | '';
21
+
7
22
  function isFieldElement(element) {
8
23
  return element instanceof Element && (element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA' || element.tagName === 'BUTTON');
9
24
  }
@@ -33,6 +48,18 @@ function getFormData(form, submitter) {
33
48
  }
34
49
  return payload;
35
50
  }
51
+ function getFormAttributes(form, submitter) {
52
+ var _ref, _submitter$getAttribu, _ref2, _submitter$getAttribu2, _submitter$getAttribu3;
53
+ var enforce = (value, list) => list.includes(value) ? value : list[0];
54
+ var action = (_ref = (_submitter$getAttribu = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formaction')) !== null && _submitter$getAttribu !== void 0 ? _submitter$getAttribu : form.getAttribute('action')) !== null && _ref !== void 0 ? _ref : "".concat(location.pathname).concat(location.search);
55
+ var method = (_ref2 = (_submitter$getAttribu2 = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formmethod')) !== null && _submitter$getAttribu2 !== void 0 ? _submitter$getAttribu2 : form.getAttribute('method')) !== null && _ref2 !== void 0 ? _ref2 : 'get';
56
+ var encType = (_submitter$getAttribu3 = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formenctype')) !== null && _submitter$getAttribu3 !== void 0 ? _submitter$getAttribu3 : form.enctype;
57
+ return {
58
+ action,
59
+ encType: enforce(encType, ['application/x-www-form-urlencoded', 'multipart/form-data']),
60
+ method: enforce(method, ['get', 'post', 'put', 'patch', 'delete'])
61
+ };
62
+ }
36
63
  function getName(paths) {
37
64
  return paths.reduce((name, path) => {
38
65
  if (typeof path === 'number') {
@@ -44,57 +71,70 @@ function getName(paths) {
44
71
  return [name, path].join('.');
45
72
  }, '');
46
73
  }
47
- function shouldValidate(submission, name) {
48
- return submission.type === 'submit' || submission.type === 'validate' && (submission.intent === '' || submission.intent === name);
74
+ function shouldValidate(intent, name) {
75
+ var _parseListCommand;
76
+ var [type] = intent.split('/', 1);
77
+ switch (type) {
78
+ case 'validate':
79
+ return intent === 'validate' || intent === "validate/".concat(name);
80
+ case 'list':
81
+ return ((_parseListCommand = parseListCommand(intent)) === null || _parseListCommand === void 0 ? void 0 : _parseListCommand.scope) === name;
82
+ default:
83
+ return true;
84
+ }
85
+ }
86
+ function getValidationMessage(errors) {
87
+ return [].concat(errors !== null && errors !== void 0 ? errors : []).join(String.fromCharCode(31));
49
88
  }
50
- function hasError(error, name) {
51
- return typeof error.find(_ref => {
52
- var [fieldName, message] = _ref;
53
- return (typeof name === 'undefined' || name === fieldName) && message !== '';
54
- }) !== 'undefined';
89
+ function getErrors(message) {
90
+ if (!message) {
91
+ return [];
92
+ }
93
+ return message.split(String.fromCharCode(31));
55
94
  }
95
+ var VALIDATION_UNDEFINED = '__undefined__';
96
+ var VALIDATION_SKIPPED = '__skipped__';
56
97
  function reportSubmission(form, submission) {
57
- var messageByName = new Map();
58
- for (var [_name, message] of submission.error) {
59
- if (!messageByName.has(_name)) {
60
- // Only keep the first error message (for now)
61
- messageByName.set(_name, message);
98
+ for (var [_name, message] of Object.entries(submission.error)) {
99
+ // There is no need to create a placeholder button if all we want is to reset the error
100
+ if (message === '') {
101
+ continue;
102
+ }
62
103
 
63
- // We can't use empty string as button name
64
- // As `form.element.namedItem('')` will always returns null
65
- var elementName = _name ? _name : '__form__';
66
- var item = form.elements.namedItem(elementName);
67
- if (item instanceof RadioNodeList) {
68
- for (var field of item) {
69
- if (field.type !== 'radio') {
70
- throw new Error('Repeated field name is not supported');
71
- }
104
+ // We can't use empty string as button name
105
+ // As `form.element.namedItem('')` will always returns null
106
+ var elementName = _name ? _name : '__form__';
107
+ var item = form.elements.namedItem(elementName);
108
+ if (item instanceof RadioNodeList) {
109
+ for (var field of item) {
110
+ if (field.type !== 'radio') {
111
+ console.warn('Repeated field name is not supported.');
112
+ continue;
72
113
  }
73
114
  }
74
- if (item === null) {
75
- // Create placeholder button to keep the error without contributing to the form data
76
- var button = document.createElement('button');
77
- button.name = elementName;
78
- button.hidden = true;
79
- button.dataset.conformTouched = 'true';
80
- item = button;
81
- form.appendChild(button);
82
- }
115
+ }
116
+ if (item === null) {
117
+ // Create placeholder button to keep the error without contributing to the form data
118
+ var button = document.createElement('button');
119
+ button.name = elementName;
120
+ button.hidden = true;
121
+ button.dataset.conformTouched = 'true';
122
+ form.appendChild(button);
83
123
  }
84
124
  }
85
125
  for (var element of form.elements) {
86
126
  if (isFieldElement(element) && element.willValidate) {
87
127
  var _elementName = element.name !== '__form__' ? element.name : '';
88
- var _message = messageByName.get(_elementName);
89
- var elementShouldValidate = shouldValidate(submission, _elementName);
128
+ var _message = submission.error[_elementName];
129
+ var elementShouldValidate = shouldValidate(submission.intent, _elementName);
90
130
  if (elementShouldValidate) {
91
131
  element.dataset.conformTouched = 'true';
92
132
  }
93
- if (typeof _message !== 'undefined' || elementShouldValidate) {
133
+ if (typeof _message === 'undefined' || ![].concat(_message).includes(VALIDATION_SKIPPED)) {
94
134
  var invalidEvent = new Event('invalid', {
95
135
  cancelable: true
96
136
  });
97
- element.setCustomValidity(_message !== null && _message !== void 0 ? _message : '');
137
+ element.setCustomValidity(getValidationMessage(_message));
98
138
  element.dispatchEvent(invalidEvent);
99
139
  }
100
140
  if (elementShouldValidate && !element.validity.valid) {
@@ -133,15 +173,15 @@ function requestSubmit(form, submitter) {
133
173
  }
134
174
 
135
175
  /**
136
- * Creates a command button on demand and trigger a form submit by clicking it.
176
+ * Creates an intent button on demand and trigger a form submit by clicking it.
137
177
  */
138
- function requestCommand(form, buttonProps) {
178
+ function requestIntent(form, buttonProps) {
139
179
  if (!form) {
140
180
  console.warn('No form element is provided');
141
181
  return;
142
182
  }
143
183
  var button = document.createElement('button');
144
- button.name = buttonProps.name;
184
+ button.name = '__intent__';
145
185
  button.value = buttonProps.value;
146
186
  button.hidden = true;
147
187
  if (buttonProps.formNoValidate) {
@@ -159,8 +199,8 @@ function requestCommand(form, buttonProps) {
159
199
  */
160
200
  function validate(field) {
161
201
  return {
162
- name: 'conform/validate',
163
- value: field !== null && field !== void 0 ? field : '',
202
+ name: '__intent__',
203
+ value: field ? "validate/".concat(field) : 'validate',
164
204
  formNoValidate: true
165
205
  };
166
206
  }
@@ -178,70 +218,80 @@ function focus(field) {
178
218
  }
179
219
  field.focus();
180
220
  }
181
- function getSubmissionType(name) {
182
- var prefix = 'conform/';
183
- if (!name.startsWith(prefix) || name.length <= prefix.length) {
184
- return null;
185
- }
186
- return name.slice(prefix.length);
187
- }
188
- function parse(payload) {
189
- var hasCommand = false;
221
+ function parse(payload, options) {
190
222
  var submission = {
191
- type: 'submit',
192
- value: {},
193
- error: []
223
+ intent: 'submit',
224
+ payload: {},
225
+ error: {}
194
226
  };
195
- try {
196
- var _loop = function _loop(value, _name2) {
197
- var submissionType = getSubmissionType(_name2);
198
- if (submissionType) {
199
- if (typeof value !== 'string') {
200
- throw new Error('The conform command could not be used on a file input');
201
- }
202
- if (hasCommand) {
203
- throw new Error('The conform command could only be set on a button');
204
- }
205
- submission = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, submission), {}, {
206
- type: submissionType,
207
- intent: value
208
- });
209
- hasCommand = true;
210
- } else {
211
- var paths = getPaths(_name2);
212
- setValue(submission.value, paths, prev => {
213
- if (!prev) {
214
- return value;
215
- } else if (Array.isArray(prev)) {
216
- return prev.concat(value);
217
- } else {
218
- return [prev, value];
219
- }
220
- });
227
+ var _loop = function _loop(_value) {
228
+ if (_name2 === '__intent__') {
229
+ if (typeof _value !== 'string' || submission.intent !== 'submit') {
230
+ throw new Error('The intent could only be set on a button');
221
231
  }
222
- };
223
- for (var [_name2, value] of payload.entries()) {
224
- _loop(value, _name2);
225
- }
226
- switch (submission.type) {
227
- case 'list':
228
- submission = handleList(submission);
229
- break;
232
+ submission.intent = _value;
233
+ } else {
234
+ var _paths = getPaths(_name2);
235
+ setValue(submission.payload, _paths, prev => {
236
+ if (!prev) {
237
+ return _value;
238
+ } else if (Array.isArray(prev)) {
239
+ return prev.concat(_value);
240
+ } else {
241
+ return [prev, _value];
242
+ }
243
+ });
230
244
  }
231
- } catch (e) {
232
- submission.error.push(['', e instanceof Error ? e.message : 'Invalid payload received']);
245
+ };
246
+ for (var [_name2, _value] of payload.entries()) {
247
+ _loop(_value);
248
+ }
249
+ var command = parseListCommand(submission.intent);
250
+ if (command) {
251
+ var paths = getPaths(command.scope);
252
+ setValue(submission.payload, paths, list => {
253
+ if (typeof list !== 'undefined' && !Array.isArray(list)) {
254
+ throw new Error('The list command can only be applied to a list');
255
+ }
256
+ return updateList(list !== null && list !== void 0 ? list : [], command);
257
+ });
258
+ }
259
+ if (typeof (options === null || options === void 0 ? void 0 : options.resolve) === 'undefined') {
260
+ return submission;
233
261
  }
234
- return submission;
262
+ var result = options.resolve(submission.payload, submission.intent);
263
+ var mergeResolveResult = resolved => {
264
+ var result = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, submission), resolved), {}, {
265
+ toJSON() {
266
+ return {
267
+ intent: this.intent,
268
+ payload: this.payload,
269
+ error: this.error
270
+ };
271
+ }
272
+ });
273
+ return result;
274
+ };
275
+ if (result instanceof Promise) {
276
+ return result.then(mergeResolveResult);
277
+ }
278
+ return mergeResolveResult(result);
235
279
  }
236
- function parseListCommand(data) {
280
+ function parseListCommand(intent) {
237
281
  try {
238
- var command = JSON.parse(data);
239
- if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder', 'combine'].includes(command.type)) {
240
- throw new Error("Unknown list command received: ".concat(command.type));
282
+ var [group, type, scope, json] = intent.split('/');
283
+ if (group !== 'list' || !['prepend', 'append', 'replace', 'remove', 'reorder'].includes(type) || !scope) {
284
+ return null;
241
285
  }
242
- return command;
286
+ var _payload = JSON.parse(json);
287
+ return {
288
+ // @ts-expect-error
289
+ type,
290
+ scope,
291
+ payload: _payload
292
+ };
243
293
  } catch (error) {
244
- throw new Error("Invalid list command: \"".concat(data, "\"; ").concat(error));
294
+ return null;
245
295
  }
246
296
  }
247
297
  function updateList(list, command) {
@@ -272,21 +322,6 @@ function updateList(list, command) {
272
322
  }
273
323
  return list;
274
324
  }
275
- function handleList(submission) {
276
- var _submission$intent;
277
- if (submission.type !== 'list') {
278
- return submission;
279
- }
280
- var command = parseListCommand((_submission$intent = submission.intent) !== null && _submission$intent !== void 0 ? _submission$intent : '');
281
- var paths = getPaths(command.scope);
282
- setValue(submission.value, paths, list => {
283
- if (typeof list !== 'undefined' && !Array.isArray(list)) {
284
- throw new Error('The list command can only be applied to a list');
285
- }
286
- return updateList(list !== null && list !== void 0 ? list : [], command);
287
- });
288
- return submission;
289
- }
290
325
  /**
291
326
  * Helpers to configure a command button for modifying a list
292
327
  *
@@ -303,12 +338,8 @@ var list = new Proxy({}, {
303
338
  return function (scope) {
304
339
  var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
305
340
  return {
306
- name: 'conform/list',
307
- value: JSON.stringify({
308
- type,
309
- scope,
310
- payload
311
- }),
341
+ name: '__intent__',
342
+ value: "list/".concat(type, "/").concat(scope, "/").concat(JSON.stringify(payload)),
312
343
  formNoValidate: true
313
344
  };
314
345
  };
@@ -316,23 +347,107 @@ var list = new Proxy({}, {
316
347
  }
317
348
  });
318
349
 
350
+ /**
351
+ * Validate the form with the Constraint Validation API
352
+ * @see https://conform.guide/api/react#validateconstraint
353
+ */
354
+ function validateConstraint(options) {
355
+ var _options$formData, _options$formatMessag;
356
+ var formData = (_options$formData = options === null || options === void 0 ? void 0 : options.formData) !== null && _options$formData !== void 0 ? _options$formData : new FormData(options.form);
357
+ var getDefaultErrors = (validity, result) => {
358
+ var errors = [];
359
+ if (validity.valueMissing) errors.push('required');
360
+ if (validity.typeMismatch || validity.badInput) errors.push('type');
361
+ if (validity.tooShort) errors.push('minLength');
362
+ if (validity.rangeUnderflow) errors.push('min');
363
+ if (validity.stepMismatch) errors.push('step');
364
+ if (validity.tooLong) errors.push('maxLength');
365
+ if (validity.rangeOverflow) errors.push('max');
366
+ if (validity.patternMismatch) errors.push('pattern');
367
+ for (var [constraintName, valid] of Object.entries(result)) {
368
+ if (!valid) {
369
+ errors.push(constraintName);
370
+ }
371
+ }
372
+ return errors;
373
+ };
374
+ var formatMessages = (_options$formatMessag = options === null || options === void 0 ? void 0 : options.formatMessages) !== null && _options$formatMessag !== void 0 ? _options$formatMessag : _ref3 => {
375
+ var {
376
+ defaultErrors
377
+ } = _ref3;
378
+ return defaultErrors;
379
+ };
380
+ return parse(formData, {
381
+ resolve(payload, intent) {
382
+ var error = {};
383
+ var constraintPattern = /^constraint[A-Z][^A-Z]*$/;
384
+ var _loop2 = function _loop2(element) {
385
+ if (isFieldElement(element)) {
386
+ var _options$acceptMultip, _options$acceptMultip2;
387
+ var _name3 = element.name === '__form__' ? '' : element.name;
388
+ var constraint = Object.entries(element.dataset).reduce((result, _ref4) => {
389
+ var [name, attributeValue = ''] = _ref4;
390
+ if (constraintPattern.test(name)) {
391
+ var _options$constraint;
392
+ var constraintName = name.slice(10).toLowerCase();
393
+ var _validate = (_options$constraint = options.constraint) === null || _options$constraint === void 0 ? void 0 : _options$constraint[constraintName];
394
+ if (typeof _validate === 'function') {
395
+ result[constraintName] = _validate(element.value, {
396
+ formData,
397
+ attributeValue
398
+ });
399
+ } else {
400
+ console.warn("Found an \"".concat(constraintName, "\" constraint with undefined definition; Please specify it on the validateConstraint API."));
401
+ }
402
+ }
403
+ return result;
404
+ }, {});
405
+ var errors = formatMessages({
406
+ name: _name3,
407
+ validity: element.validity,
408
+ constraint,
409
+ defaultErrors: getDefaultErrors(element.validity, constraint)
410
+ });
411
+ var shouldAcceptMultipleErrors = (_options$acceptMultip = options === null || options === void 0 ? void 0 : (_options$acceptMultip2 = options.acceptMultipleErrors) === null || _options$acceptMultip2 === void 0 ? void 0 : _options$acceptMultip2.call(options, {
412
+ name: _name3,
413
+ payload,
414
+ intent
415
+ })) !== null && _options$acceptMultip !== void 0 ? _options$acceptMultip : false;
416
+ if (errors.length > 0) {
417
+ error[_name3] = shouldAcceptMultipleErrors ? errors : errors[0];
418
+ }
419
+ }
420
+ };
421
+ for (var element of options.form.elements) {
422
+ _loop2(element);
423
+ }
424
+ return {
425
+ error
426
+ };
427
+ }
428
+ });
429
+ }
430
+
431
+ exports.VALIDATION_SKIPPED = VALIDATION_SKIPPED;
432
+ exports.VALIDATION_UNDEFINED = VALIDATION_UNDEFINED;
319
433
  exports.focus = focus;
434
+ exports.getErrors = getErrors;
435
+ exports.getFormAttributes = getFormAttributes;
320
436
  exports.getFormData = getFormData;
321
437
  exports.getFormElement = getFormElement;
322
438
  exports.getFormElements = getFormElements;
323
439
  exports.getName = getName;
324
440
  exports.getPaths = getPaths;
325
- exports.getSubmissionType = getSubmissionType;
326
- exports.handleList = handleList;
327
- exports.hasError = hasError;
441
+ exports.getValidationMessage = getValidationMessage;
328
442
  exports.isFieldElement = isFieldElement;
329
443
  exports.list = list;
330
444
  exports.parse = parse;
331
445
  exports.parseListCommand = parseListCommand;
332
446
  exports.reportSubmission = reportSubmission;
333
- exports.requestCommand = requestCommand;
447
+ exports.requestIntent = requestIntent;
334
448
  exports.requestSubmit = requestSubmit;
335
449
  exports.setValue = setValue;
336
450
  exports.shouldValidate = shouldValidate;
337
451
  exports.updateList = updateList;
338
452
  exports.validate = validate;
453
+ exports.validateConstraint = validateConstraint;
@@ -20,6 +20,7 @@ function _objectSpread2(target) {
20
20
  return target;
21
21
  }
22
22
  function _defineProperty(obj, key, value) {
23
+ key = _toPropertyKey(key);
23
24
  if (key in obj) {
24
25
  Object.defineProperty(obj, key, {
25
26
  value: value,
@@ -32,5 +33,19 @@ function _defineProperty(obj, key, value) {
32
33
  }
33
34
  return obj;
34
35
  }
36
+ function _toPrimitive(input, hint) {
37
+ if (typeof input !== "object" || input === null) return input;
38
+ var prim = input[Symbol.toPrimitive];
39
+ if (prim !== undefined) {
40
+ var res = prim.call(input, hint || "default");
41
+ if (typeof res !== "object") return res;
42
+ throw new TypeError("@@toPrimitive must return a primitive value.");
43
+ }
44
+ return (hint === "string" ? String : Number)(input);
45
+ }
46
+ function _toPropertyKey(arg) {
47
+ var key = _toPrimitive(arg, "string");
48
+ return typeof key === "symbol" ? key : String(key);
49
+ }
35
50
 
36
- export { _defineProperty as defineProperty, _objectSpread2 as objectSpread2 };
51
+ export { _defineProperty as defineProperty, _objectSpread2 as objectSpread2, _toPrimitive as toPrimitive, _toPropertyKey as toPropertyKey };
package/module/index.js CHANGED
@@ -1,5 +1,20 @@
1
1
  import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.js';
2
2
 
3
+ // type Join<K, P> = P extends string | number ?
4
+ // K extends string | number ?
5
+ // `${K}${"" extends P ? "" : "."}${P}`
6
+ // : never : never;
7
+
8
+ // type DottedPaths<T> = T extends object ?
9
+ // { [K in keyof T]-?: K extends string | number ?
10
+ // `${K}` | Join<K, DottedPaths<T[K]>>
11
+ // : never
12
+ // }[keyof T] : ""
13
+
14
+ // type Pathfix<T> = T extends `${infer Prefix}.${number}${infer Postfix}` ? `${Prefix}[${number}]${Pathfix<Postfix>}` : T;
15
+
16
+ // type Path<Schema> = Pathfix<DottedPaths<Schema>> | '';
17
+
3
18
  function isFieldElement(element) {
4
19
  return element instanceof Element && (element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA' || element.tagName === 'BUTTON');
5
20
  }
@@ -29,6 +44,18 @@ function getFormData(form, submitter) {
29
44
  }
30
45
  return payload;
31
46
  }
47
+ function getFormAttributes(form, submitter) {
48
+ var _ref, _submitter$getAttribu, _ref2, _submitter$getAttribu2, _submitter$getAttribu3;
49
+ var enforce = (value, list) => list.includes(value) ? value : list[0];
50
+ var action = (_ref = (_submitter$getAttribu = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formaction')) !== null && _submitter$getAttribu !== void 0 ? _submitter$getAttribu : form.getAttribute('action')) !== null && _ref !== void 0 ? _ref : "".concat(location.pathname).concat(location.search);
51
+ var method = (_ref2 = (_submitter$getAttribu2 = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formmethod')) !== null && _submitter$getAttribu2 !== void 0 ? _submitter$getAttribu2 : form.getAttribute('method')) !== null && _ref2 !== void 0 ? _ref2 : 'get';
52
+ var encType = (_submitter$getAttribu3 = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formenctype')) !== null && _submitter$getAttribu3 !== void 0 ? _submitter$getAttribu3 : form.enctype;
53
+ return {
54
+ action,
55
+ encType: enforce(encType, ['application/x-www-form-urlencoded', 'multipart/form-data']),
56
+ method: enforce(method, ['get', 'post', 'put', 'patch', 'delete'])
57
+ };
58
+ }
32
59
  function getName(paths) {
33
60
  return paths.reduce((name, path) => {
34
61
  if (typeof path === 'number') {
@@ -40,57 +67,70 @@ function getName(paths) {
40
67
  return [name, path].join('.');
41
68
  }, '');
42
69
  }
43
- function shouldValidate(submission, name) {
44
- return submission.type === 'submit' || submission.type === 'validate' && (submission.intent === '' || submission.intent === name);
70
+ function shouldValidate(intent, name) {
71
+ var _parseListCommand;
72
+ var [type] = intent.split('/', 1);
73
+ switch (type) {
74
+ case 'validate':
75
+ return intent === 'validate' || intent === "validate/".concat(name);
76
+ case 'list':
77
+ return ((_parseListCommand = parseListCommand(intent)) === null || _parseListCommand === void 0 ? void 0 : _parseListCommand.scope) === name;
78
+ default:
79
+ return true;
80
+ }
81
+ }
82
+ function getValidationMessage(errors) {
83
+ return [].concat(errors !== null && errors !== void 0 ? errors : []).join(String.fromCharCode(31));
45
84
  }
46
- function hasError(error, name) {
47
- return typeof error.find(_ref => {
48
- var [fieldName, message] = _ref;
49
- return (typeof name === 'undefined' || name === fieldName) && message !== '';
50
- }) !== 'undefined';
85
+ function getErrors(message) {
86
+ if (!message) {
87
+ return [];
88
+ }
89
+ return message.split(String.fromCharCode(31));
51
90
  }
91
+ var VALIDATION_UNDEFINED = '__undefined__';
92
+ var VALIDATION_SKIPPED = '__skipped__';
52
93
  function reportSubmission(form, submission) {
53
- var messageByName = new Map();
54
- for (var [_name, message] of submission.error) {
55
- if (!messageByName.has(_name)) {
56
- // Only keep the first error message (for now)
57
- messageByName.set(_name, message);
94
+ for (var [_name, message] of Object.entries(submission.error)) {
95
+ // There is no need to create a placeholder button if all we want is to reset the error
96
+ if (message === '') {
97
+ continue;
98
+ }
58
99
 
59
- // We can't use empty string as button name
60
- // As `form.element.namedItem('')` will always returns null
61
- var elementName = _name ? _name : '__form__';
62
- var item = form.elements.namedItem(elementName);
63
- if (item instanceof RadioNodeList) {
64
- for (var field of item) {
65
- if (field.type !== 'radio') {
66
- throw new Error('Repeated field name is not supported');
67
- }
100
+ // We can't use empty string as button name
101
+ // As `form.element.namedItem('')` will always returns null
102
+ var elementName = _name ? _name : '__form__';
103
+ var item = form.elements.namedItem(elementName);
104
+ if (item instanceof RadioNodeList) {
105
+ for (var field of item) {
106
+ if (field.type !== 'radio') {
107
+ console.warn('Repeated field name is not supported.');
108
+ continue;
68
109
  }
69
110
  }
70
- if (item === null) {
71
- // Create placeholder button to keep the error without contributing to the form data
72
- var button = document.createElement('button');
73
- button.name = elementName;
74
- button.hidden = true;
75
- button.dataset.conformTouched = 'true';
76
- item = button;
77
- form.appendChild(button);
78
- }
111
+ }
112
+ if (item === null) {
113
+ // Create placeholder button to keep the error without contributing to the form data
114
+ var button = document.createElement('button');
115
+ button.name = elementName;
116
+ button.hidden = true;
117
+ button.dataset.conformTouched = 'true';
118
+ form.appendChild(button);
79
119
  }
80
120
  }
81
121
  for (var element of form.elements) {
82
122
  if (isFieldElement(element) && element.willValidate) {
83
123
  var _elementName = element.name !== '__form__' ? element.name : '';
84
- var _message = messageByName.get(_elementName);
85
- var elementShouldValidate = shouldValidate(submission, _elementName);
124
+ var _message = submission.error[_elementName];
125
+ var elementShouldValidate = shouldValidate(submission.intent, _elementName);
86
126
  if (elementShouldValidate) {
87
127
  element.dataset.conformTouched = 'true';
88
128
  }
89
- if (typeof _message !== 'undefined' || elementShouldValidate) {
129
+ if (typeof _message === 'undefined' || ![].concat(_message).includes(VALIDATION_SKIPPED)) {
90
130
  var invalidEvent = new Event('invalid', {
91
131
  cancelable: true
92
132
  });
93
- element.setCustomValidity(_message !== null && _message !== void 0 ? _message : '');
133
+ element.setCustomValidity(getValidationMessage(_message));
94
134
  element.dispatchEvent(invalidEvent);
95
135
  }
96
136
  if (elementShouldValidate && !element.validity.valid) {
@@ -129,15 +169,15 @@ function requestSubmit(form, submitter) {
129
169
  }
130
170
 
131
171
  /**
132
- * Creates a command button on demand and trigger a form submit by clicking it.
172
+ * Creates an intent button on demand and trigger a form submit by clicking it.
133
173
  */
134
- function requestCommand(form, buttonProps) {
174
+ function requestIntent(form, buttonProps) {
135
175
  if (!form) {
136
176
  console.warn('No form element is provided');
137
177
  return;
138
178
  }
139
179
  var button = document.createElement('button');
140
- button.name = buttonProps.name;
180
+ button.name = '__intent__';
141
181
  button.value = buttonProps.value;
142
182
  button.hidden = true;
143
183
  if (buttonProps.formNoValidate) {
@@ -155,8 +195,8 @@ function requestCommand(form, buttonProps) {
155
195
  */
156
196
  function validate(field) {
157
197
  return {
158
- name: 'conform/validate',
159
- value: field !== null && field !== void 0 ? field : '',
198
+ name: '__intent__',
199
+ value: field ? "validate/".concat(field) : 'validate',
160
200
  formNoValidate: true
161
201
  };
162
202
  }
@@ -174,70 +214,80 @@ function focus(field) {
174
214
  }
175
215
  field.focus();
176
216
  }
177
- function getSubmissionType(name) {
178
- var prefix = 'conform/';
179
- if (!name.startsWith(prefix) || name.length <= prefix.length) {
180
- return null;
181
- }
182
- return name.slice(prefix.length);
183
- }
184
- function parse(payload) {
185
- var hasCommand = false;
217
+ function parse(payload, options) {
186
218
  var submission = {
187
- type: 'submit',
188
- value: {},
189
- error: []
219
+ intent: 'submit',
220
+ payload: {},
221
+ error: {}
190
222
  };
191
- try {
192
- var _loop = function _loop(value, _name2) {
193
- var submissionType = getSubmissionType(_name2);
194
- if (submissionType) {
195
- if (typeof value !== 'string') {
196
- throw new Error('The conform command could not be used on a file input');
197
- }
198
- if (hasCommand) {
199
- throw new Error('The conform command could only be set on a button');
200
- }
201
- submission = _objectSpread2(_objectSpread2({}, submission), {}, {
202
- type: submissionType,
203
- intent: value
204
- });
205
- hasCommand = true;
206
- } else {
207
- var paths = getPaths(_name2);
208
- setValue(submission.value, paths, prev => {
209
- if (!prev) {
210
- return value;
211
- } else if (Array.isArray(prev)) {
212
- return prev.concat(value);
213
- } else {
214
- return [prev, value];
215
- }
216
- });
223
+ var _loop = function _loop(_value) {
224
+ if (_name2 === '__intent__') {
225
+ if (typeof _value !== 'string' || submission.intent !== 'submit') {
226
+ throw new Error('The intent could only be set on a button');
217
227
  }
218
- };
219
- for (var [_name2, value] of payload.entries()) {
220
- _loop(value, _name2);
221
- }
222
- switch (submission.type) {
223
- case 'list':
224
- submission = handleList(submission);
225
- break;
228
+ submission.intent = _value;
229
+ } else {
230
+ var _paths = getPaths(_name2);
231
+ setValue(submission.payload, _paths, prev => {
232
+ if (!prev) {
233
+ return _value;
234
+ } else if (Array.isArray(prev)) {
235
+ return prev.concat(_value);
236
+ } else {
237
+ return [prev, _value];
238
+ }
239
+ });
226
240
  }
227
- } catch (e) {
228
- submission.error.push(['', e instanceof Error ? e.message : 'Invalid payload received']);
241
+ };
242
+ for (var [_name2, _value] of payload.entries()) {
243
+ _loop(_value);
244
+ }
245
+ var command = parseListCommand(submission.intent);
246
+ if (command) {
247
+ var paths = getPaths(command.scope);
248
+ setValue(submission.payload, paths, list => {
249
+ if (typeof list !== 'undefined' && !Array.isArray(list)) {
250
+ throw new Error('The list command can only be applied to a list');
251
+ }
252
+ return updateList(list !== null && list !== void 0 ? list : [], command);
253
+ });
254
+ }
255
+ if (typeof (options === null || options === void 0 ? void 0 : options.resolve) === 'undefined') {
256
+ return submission;
229
257
  }
230
- return submission;
258
+ var result = options.resolve(submission.payload, submission.intent);
259
+ var mergeResolveResult = resolved => {
260
+ var result = _objectSpread2(_objectSpread2(_objectSpread2({}, submission), resolved), {}, {
261
+ toJSON() {
262
+ return {
263
+ intent: this.intent,
264
+ payload: this.payload,
265
+ error: this.error
266
+ };
267
+ }
268
+ });
269
+ return result;
270
+ };
271
+ if (result instanceof Promise) {
272
+ return result.then(mergeResolveResult);
273
+ }
274
+ return mergeResolveResult(result);
231
275
  }
232
- function parseListCommand(data) {
276
+ function parseListCommand(intent) {
233
277
  try {
234
- var command = JSON.parse(data);
235
- if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder', 'combine'].includes(command.type)) {
236
- throw new Error("Unknown list command received: ".concat(command.type));
278
+ var [group, type, scope, json] = intent.split('/');
279
+ if (group !== 'list' || !['prepend', 'append', 'replace', 'remove', 'reorder'].includes(type) || !scope) {
280
+ return null;
237
281
  }
238
- return command;
282
+ var _payload = JSON.parse(json);
283
+ return {
284
+ // @ts-expect-error
285
+ type,
286
+ scope,
287
+ payload: _payload
288
+ };
239
289
  } catch (error) {
240
- throw new Error("Invalid list command: \"".concat(data, "\"; ").concat(error));
290
+ return null;
241
291
  }
242
292
  }
243
293
  function updateList(list, command) {
@@ -268,21 +318,6 @@ function updateList(list, command) {
268
318
  }
269
319
  return list;
270
320
  }
271
- function handleList(submission) {
272
- var _submission$intent;
273
- if (submission.type !== 'list') {
274
- return submission;
275
- }
276
- var command = parseListCommand((_submission$intent = submission.intent) !== null && _submission$intent !== void 0 ? _submission$intent : '');
277
- var paths = getPaths(command.scope);
278
- setValue(submission.value, paths, list => {
279
- if (typeof list !== 'undefined' && !Array.isArray(list)) {
280
- throw new Error('The list command can only be applied to a list');
281
- }
282
- return updateList(list !== null && list !== void 0 ? list : [], command);
283
- });
284
- return submission;
285
- }
286
321
  /**
287
322
  * Helpers to configure a command button for modifying a list
288
323
  *
@@ -299,12 +334,8 @@ var list = new Proxy({}, {
299
334
  return function (scope) {
300
335
  var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
301
336
  return {
302
- name: 'conform/list',
303
- value: JSON.stringify({
304
- type,
305
- scope,
306
- payload
307
- }),
337
+ name: '__intent__',
338
+ value: "list/".concat(type, "/").concat(scope, "/").concat(JSON.stringify(payload)),
308
339
  formNoValidate: true
309
340
  };
310
341
  };
@@ -312,4 +343,85 @@ var list = new Proxy({}, {
312
343
  }
313
344
  });
314
345
 
315
- export { focus, getFormData, getFormElement, getFormElements, getName, getPaths, getSubmissionType, handleList, hasError, isFieldElement, list, parse, parseListCommand, reportSubmission, requestCommand, requestSubmit, setValue, shouldValidate, updateList, validate };
346
+ /**
347
+ * Validate the form with the Constraint Validation API
348
+ * @see https://conform.guide/api/react#validateconstraint
349
+ */
350
+ function validateConstraint(options) {
351
+ var _options$formData, _options$formatMessag;
352
+ var formData = (_options$formData = options === null || options === void 0 ? void 0 : options.formData) !== null && _options$formData !== void 0 ? _options$formData : new FormData(options.form);
353
+ var getDefaultErrors = (validity, result) => {
354
+ var errors = [];
355
+ if (validity.valueMissing) errors.push('required');
356
+ if (validity.typeMismatch || validity.badInput) errors.push('type');
357
+ if (validity.tooShort) errors.push('minLength');
358
+ if (validity.rangeUnderflow) errors.push('min');
359
+ if (validity.stepMismatch) errors.push('step');
360
+ if (validity.tooLong) errors.push('maxLength');
361
+ if (validity.rangeOverflow) errors.push('max');
362
+ if (validity.patternMismatch) errors.push('pattern');
363
+ for (var [constraintName, valid] of Object.entries(result)) {
364
+ if (!valid) {
365
+ errors.push(constraintName);
366
+ }
367
+ }
368
+ return errors;
369
+ };
370
+ var formatMessages = (_options$formatMessag = options === null || options === void 0 ? void 0 : options.formatMessages) !== null && _options$formatMessag !== void 0 ? _options$formatMessag : _ref3 => {
371
+ var {
372
+ defaultErrors
373
+ } = _ref3;
374
+ return defaultErrors;
375
+ };
376
+ return parse(formData, {
377
+ resolve(payload, intent) {
378
+ var error = {};
379
+ var constraintPattern = /^constraint[A-Z][^A-Z]*$/;
380
+ var _loop2 = function _loop2(element) {
381
+ if (isFieldElement(element)) {
382
+ var _options$acceptMultip, _options$acceptMultip2;
383
+ var _name3 = element.name === '__form__' ? '' : element.name;
384
+ var constraint = Object.entries(element.dataset).reduce((result, _ref4) => {
385
+ var [name, attributeValue = ''] = _ref4;
386
+ if (constraintPattern.test(name)) {
387
+ var _options$constraint;
388
+ var constraintName = name.slice(10).toLowerCase();
389
+ var _validate = (_options$constraint = options.constraint) === null || _options$constraint === void 0 ? void 0 : _options$constraint[constraintName];
390
+ if (typeof _validate === 'function') {
391
+ result[constraintName] = _validate(element.value, {
392
+ formData,
393
+ attributeValue
394
+ });
395
+ } else {
396
+ console.warn("Found an \"".concat(constraintName, "\" constraint with undefined definition; Please specify it on the validateConstraint API."));
397
+ }
398
+ }
399
+ return result;
400
+ }, {});
401
+ var errors = formatMessages({
402
+ name: _name3,
403
+ validity: element.validity,
404
+ constraint,
405
+ defaultErrors: getDefaultErrors(element.validity, constraint)
406
+ });
407
+ var shouldAcceptMultipleErrors = (_options$acceptMultip = options === null || options === void 0 ? void 0 : (_options$acceptMultip2 = options.acceptMultipleErrors) === null || _options$acceptMultip2 === void 0 ? void 0 : _options$acceptMultip2.call(options, {
408
+ name: _name3,
409
+ payload,
410
+ intent
411
+ })) !== null && _options$acceptMultip !== void 0 ? _options$acceptMultip : false;
412
+ if (errors.length > 0) {
413
+ error[_name3] = shouldAcceptMultipleErrors ? errors : errors[0];
414
+ }
415
+ }
416
+ };
417
+ for (var element of options.form.elements) {
418
+ _loop2(element);
419
+ }
420
+ return {
421
+ error
422
+ };
423
+ }
424
+ });
425
+ }
426
+
427
+ export { VALIDATION_SKIPPED, VALIDATION_UNDEFINED, focus, getErrors, getFormAttributes, getFormData, getFormElement, getFormElements, getName, getPaths, getValidationMessage, isFieldElement, list, parse, parseListCommand, reportSubmission, requestIntent, requestSubmit, setValue, shouldValidate, updateList, validate, validateConstraint };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@conform-to/dom",
3
3
  "description": "A set of opinionated helpers built on top of the Constraint Validation API",
4
4
  "license": "MIT",
5
- "version": "0.5.0",
5
+ "version": "0.6.0-pre.0",
6
6
  "main": "index.js",
7
7
  "module": "module/index.js",
8
8
  "repository": {