@conform-to/dom 1.8.2 → 1.9.1
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.md +11 -11
- package/dist/dom.d.ts +31 -2
- package/dist/dom.js +91 -24
- package/dist/dom.mjs +85 -22
- package/dist/form.js +37 -32
- package/dist/form.mjs +34 -29
- package/dist/formdata.d.ts +122 -75
- package/dist/formdata.js +341 -241
- package/dist/formdata.mjs +328 -225
- package/dist/future/index.d.ts +5 -0
- package/dist/future/index.js +38 -0
- package/dist/future/index.mjs +3 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -9
- package/dist/index.mjs +2 -2
- package/dist/submission.d.ts +13 -0
- package/dist/submission.js +81 -15
- package/dist/submission.mjs +77 -13
- package/dist/types.d.ts +99 -0
- package/dist/util.d.ts +11 -0
- package/dist/util.js +67 -0
- package/dist/util.mjs +65 -1
- package/package.json +8 -1
package/dist/formdata.mjs
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.mjs';
|
|
2
|
+
import { isSubmitter, isGlobalInstance } from './dom.mjs';
|
|
3
|
+
import { isPlainObject, stripFiles, deepEqual } from './util.mjs';
|
|
4
|
+
|
|
5
|
+
var DEFAULT_INTENT_NAME = '__INTENT__';
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
8
|
* Construct a form data with the submitter value.
|
|
@@ -9,253 +13,231 @@ import { INTENT } from './submission.mjs';
|
|
|
9
13
|
*/
|
|
10
14
|
function getFormData(form, submitter) {
|
|
11
15
|
var payload = new FormData(form, submitter);
|
|
12
|
-
if (submitter
|
|
13
|
-
|
|
16
|
+
if (submitter) {
|
|
17
|
+
if (!isSubmitter(submitter)) {
|
|
18
|
+
throw new TypeError('The submitter must be an input or button element with type submit.');
|
|
19
|
+
}
|
|
20
|
+
if (submitter.name) {
|
|
21
|
+
var entries = payload.getAll(submitter.name);
|
|
14
22
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
// This assumes the submitter value to be always unique, which should be fine in most cases
|
|
24
|
+
if (!entries.includes(submitter.value)) {
|
|
25
|
+
payload.append(submitter.name, submitter.value);
|
|
26
|
+
}
|
|
18
27
|
}
|
|
19
28
|
}
|
|
20
29
|
return payload;
|
|
21
30
|
}
|
|
22
31
|
|
|
23
32
|
/**
|
|
24
|
-
*
|
|
33
|
+
* Convert a string path into an array of segments.
|
|
34
|
+
*
|
|
25
35
|
* @example
|
|
26
36
|
* ```js
|
|
27
|
-
*
|
|
37
|
+
* getPathSegments("object.key"); // → ['object', 'key']
|
|
38
|
+
* getPathSegments("array[0].content"); // → ['array', 0, 'content']
|
|
39
|
+
* getPathSegments("todos[]"); // → ['todos', '']
|
|
28
40
|
* ```
|
|
29
41
|
*/
|
|
30
|
-
function
|
|
31
|
-
if (!
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
function getPathSegments(path) {
|
|
43
|
+
if (!path) return [];
|
|
44
|
+
var tokenRegex = /([^.[\]]+)|\[(\d*)\]/g;
|
|
45
|
+
var segments = [];
|
|
46
|
+
var lastIndex = 0,
|
|
47
|
+
match;
|
|
48
|
+
while (match = tokenRegex.exec(path)) {
|
|
49
|
+
// allow a single “.” between tokens
|
|
50
|
+
if (match.index !== lastIndex) {
|
|
51
|
+
if (!(match.index === lastIndex + 1 && path[lastIndex] === '.')) {
|
|
52
|
+
throw new Error("Invalid path syntax at position ".concat(lastIndex, " in \"").concat(path, "\""));
|
|
41
53
|
}
|
|
42
54
|
}
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
var [, key, index] = match;
|
|
56
|
+
if (key !== undefined) {
|
|
57
|
+
if (key === '__proto__' || key === 'constructor') {
|
|
58
|
+
throw new Error("Invalid path segment \"".concat(key, "\""));
|
|
59
|
+
}
|
|
60
|
+
segments.push(key);
|
|
61
|
+
} else if (index === '') {
|
|
62
|
+
segments.push('');
|
|
63
|
+
} else {
|
|
64
|
+
var number = Number(index);
|
|
65
|
+
if (!Number.isInteger(number) || number < 0) {
|
|
66
|
+
throw new Error("Invalid path segment: array index must be a non-negative integer, got ".concat(number));
|
|
67
|
+
}
|
|
68
|
+
segments.push(number);
|
|
69
|
+
}
|
|
70
|
+
lastIndex = tokenRegex.lastIndex;
|
|
71
|
+
}
|
|
72
|
+
if (lastIndex !== path.length) {
|
|
73
|
+
throw new Error("Invalid path syntax at position ".concat(lastIndex, " in \"").concat(path, "\""));
|
|
74
|
+
}
|
|
75
|
+
return segments;
|
|
45
76
|
}
|
|
46
77
|
|
|
47
78
|
/**
|
|
48
|
-
* Returns a formatted name from the
|
|
79
|
+
* Returns a formatted name from the path segments based on the dot and bracket notation.
|
|
80
|
+
*
|
|
49
81
|
* @example
|
|
50
82
|
* ```js
|
|
51
|
-
*
|
|
83
|
+
* formatPathSegments(['object', 'key']); // → "object.key"
|
|
84
|
+
* formatPathSegments(['array', 0, 'content']); // → "array[0].content"
|
|
85
|
+
* formatPathSegments(['todos', '']); // → "todos[]"
|
|
52
86
|
* ```
|
|
53
87
|
*/
|
|
54
|
-
function
|
|
55
|
-
return
|
|
56
|
-
if (typeof path === 'number') {
|
|
57
|
-
return "".concat(name, "[").concat(Number.isNaN(path) ? '' : path, "]");
|
|
58
|
-
}
|
|
59
|
-
if (name === '' || path === '') {
|
|
60
|
-
return [name, path].join('');
|
|
61
|
-
}
|
|
62
|
-
return [name, path].join('.');
|
|
63
|
-
}, '');
|
|
88
|
+
function formatPathSegments(segments) {
|
|
89
|
+
return segments.reduce((path, segment) => appendPathSegment(path, segment), '');
|
|
64
90
|
}
|
|
65
91
|
|
|
66
92
|
/**
|
|
67
|
-
*
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Check if a name match the prefix paths
|
|
75
|
-
*/
|
|
76
|
-
function isPrefix(name, prefix) {
|
|
77
|
-
var paths = getPaths(name);
|
|
78
|
-
var prefixPaths = getPaths(prefix);
|
|
79
|
-
return paths.length >= prefixPaths.length && prefixPaths.every((path, index) => paths[index] === path);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Compare the parent and child paths to get the relative paths
|
|
84
|
-
* Returns null if the child paths do not start with the parent paths
|
|
93
|
+
* Append one more segment onto an existing path string.
|
|
94
|
+
*
|
|
95
|
+
* - segment = `undefined` ⇒ no-op
|
|
96
|
+
* - segment = `""` ⇒ empty brackets "[]"
|
|
97
|
+
* - segment = `number` ⇒ bracket notation "[n]"
|
|
98
|
+
* - segment = `string` ⇒ dot-notation ".prop"
|
|
85
99
|
*/
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return childPaths.slice(parentPaths.length);
|
|
100
|
+
function appendPathSegment(path, segment) {
|
|
101
|
+
// 1) nothing to append
|
|
102
|
+
if (typeof segment === 'undefined') {
|
|
103
|
+
return path !== null && path !== void 0 ? path : '';
|
|
91
104
|
}
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
105
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
var paths = getPaths(name);
|
|
100
|
-
var length = paths.length;
|
|
101
|
-
var lastIndex = length - 1;
|
|
102
|
-
var index = -1;
|
|
103
|
-
var pointer = target;
|
|
104
|
-
while (pointer != null && ++index < length) {
|
|
105
|
-
var _key = paths[index];
|
|
106
|
-
var nextKey = paths[index + 1];
|
|
107
|
-
var newValue = index != lastIndex ? Object.prototype.hasOwnProperty.call(pointer, _key) && pointer[_key] !== null ? pointer[_key] : typeof nextKey === 'number' ? [] : {} : valueFn(pointer[_key]);
|
|
108
|
-
pointer[_key] = newValue;
|
|
109
|
-
pointer = pointer[_key];
|
|
106
|
+
// 2) explicit empty-segment => empty bracket
|
|
107
|
+
if (segment === '') {
|
|
108
|
+
// even as first segment, "[]" is valid
|
|
109
|
+
return "".concat(path, "[]");
|
|
110
110
|
}
|
|
111
|
-
}
|
|
112
111
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
function getValue(target, name) {
|
|
117
|
-
var pointer = target;
|
|
118
|
-
for (var path of getPaths(name)) {
|
|
119
|
-
if (typeof pointer === 'undefined' || pointer == null) {
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
if (!Object.prototype.hasOwnProperty.call(pointer, path)) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (isPlainObject(pointer) && typeof path === 'string') {
|
|
126
|
-
pointer = pointer[path];
|
|
127
|
-
} else if (Array.isArray(pointer) && typeof path === 'number') {
|
|
128
|
-
pointer = pointer[path];
|
|
129
|
-
} else {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
112
|
+
// 3) numeric index => [n]
|
|
113
|
+
if (typeof segment === 'number') {
|
|
114
|
+
return "".concat(path, "[").concat(segment, "]");
|
|
132
115
|
}
|
|
133
|
-
|
|
116
|
+
|
|
117
|
+
// 4) non-empty string => .prop (no leading dot if no base)
|
|
118
|
+
return path ? "".concat(path, ".").concat(segment) : segment;
|
|
134
119
|
}
|
|
135
120
|
|
|
136
121
|
/**
|
|
137
|
-
*
|
|
122
|
+
* Returns true if `prefix` is a valid leading path of `name`.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```js
|
|
126
|
+
* isPrefix("foo.bar.baz", "foo.bar") // → true
|
|
127
|
+
* isPrefix("foo.bar[3].baz", "foo.bar[3]") // → true
|
|
128
|
+
* isPrefix("foo.bar[3].baz", "foo.bar") // → true
|
|
129
|
+
* isPrefix("foo.bar[3].baz", "foo.baz") // → false
|
|
130
|
+
* isPrefix("foo", "foo.bar") // → false
|
|
131
|
+
* ```
|
|
138
132
|
*/
|
|
139
|
-
function
|
|
140
|
-
return
|
|
141
|
-
}
|
|
142
|
-
function isGlobalInstance(obj, className) {
|
|
143
|
-
var Ctor = globalThis[className];
|
|
144
|
-
return typeof Ctor === 'function' && obj instanceof Ctor;
|
|
133
|
+
function isPrefix(name, prefix) {
|
|
134
|
+
return getRelativePath(name, getPathSegments(prefix)) !== null;
|
|
145
135
|
}
|
|
146
136
|
|
|
147
137
|
/**
|
|
148
|
-
*
|
|
138
|
+
* Return the segments of `fullPathStr` that come after the `baseSegments` prefix.
|
|
139
|
+
*
|
|
140
|
+
* @param fullPathStr Full path as a dot/bracket string
|
|
141
|
+
* @param basePath Base path, already parsed into segments
|
|
142
|
+
* @returns The “tail” segments, or `null` if `fullPathStr` isn’t nested under `baseSegments`
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```js
|
|
146
|
+
* getRelativePath("foo.bar[0].qux", ["foo","bar"]) // → [0, "qux"]
|
|
147
|
+
* getRelativePath("a.b.c.d", ["a","b"]) // → ["c","d"]
|
|
148
|
+
* getRelativePath("foo", ["foo","bar"]) // → null
|
|
149
|
+
* ```
|
|
149
150
|
*/
|
|
151
|
+
function getRelativePath(name, basePath) {
|
|
152
|
+
var fullPath = getPathSegments(name);
|
|
150
153
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
var obj = Object.keys(value).sort().reduce((result, key) => {
|
|
155
|
-
var data = normalize(value[key], acceptFile);
|
|
156
|
-
if (typeof data !== 'undefined') {
|
|
157
|
-
result[key] = data;
|
|
158
|
-
}
|
|
159
|
-
return result;
|
|
160
|
-
}, {});
|
|
161
|
-
if (Object.keys(obj).length === 0) {
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
return obj;
|
|
154
|
+
// if full is at least as long *and* starts with the base…
|
|
155
|
+
if (fullPath.length >= basePath.length && basePath.every((segment, i) => segment === fullPath[i])) {
|
|
156
|
+
return fullPath.slice(basePath.length);
|
|
165
157
|
}
|
|
166
|
-
|
|
167
|
-
if (value.length === 0) {
|
|
168
|
-
return undefined;
|
|
169
|
-
}
|
|
170
|
-
return value.map(item => normalize(item, acceptFile));
|
|
171
|
-
}
|
|
172
|
-
if (typeof value === 'string' && value === '' || value === null || isGlobalInstance(value, 'File') && (!acceptFile || value.size === 0)) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
return value;
|
|
158
|
+
return null;
|
|
176
159
|
}
|
|
177
160
|
|
|
178
161
|
/**
|
|
179
|
-
*
|
|
162
|
+
* Assign a value to a target object by following the path segments.
|
|
180
163
|
*/
|
|
181
|
-
function
|
|
182
|
-
var
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (typeof value !== 'undefined') {
|
|
189
|
-
result[prefix] = value;
|
|
164
|
+
function setValueAtPath(target, pathOrSegments, valueOrFn) {
|
|
165
|
+
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
166
|
+
try {
|
|
167
|
+
// 1) normalize + validate path
|
|
168
|
+
var segments = typeof pathOrSegments === 'string' ? getPathSegments(pathOrSegments) : pathOrSegments;
|
|
169
|
+
if (segments.length === 0) {
|
|
170
|
+
throw new Error('Cannot set value at the object root');
|
|
190
171
|
}
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
process(data[i], "".concat(prefix, "[").concat(i, "]"));
|
|
194
|
-
}
|
|
195
|
-
} else if (isPlainObject(data)) {
|
|
196
|
-
for (var [_key2, _value] of Object.entries(data)) {
|
|
197
|
-
process(_value, prefix ? "".concat(prefix, ".").concat(_key2) : _key2);
|
|
198
|
-
}
|
|
172
|
+
if (segments.some((segment, i) => segment === '' && i < segments.length - 1)) {
|
|
173
|
+
throw new Error("Empty brackets '[]' only allowed at end of path (\"".concat(pathOrSegments, "\")"));
|
|
199
174
|
}
|
|
200
|
-
}
|
|
201
|
-
if (data) {
|
|
202
|
-
var _options$prefix;
|
|
203
|
-
process(data, (_options$prefix = options.prefix) !== null && _options$prefix !== void 0 ? _options$prefix : '');
|
|
204
|
-
}
|
|
205
|
-
return result;
|
|
206
|
-
}
|
|
207
|
-
function deepEqual(left, right) {
|
|
208
|
-
if (Object.is(left, right)) {
|
|
209
|
-
return true;
|
|
210
|
-
}
|
|
211
|
-
if (left == null || right == null) {
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
175
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
var
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
176
|
+
// 2) clone root if needed
|
|
177
|
+
var result = options.clone ? _objectSpread2({}, target) : target;
|
|
178
|
+
var pointer = result;
|
|
179
|
+
|
|
180
|
+
// 3) drill down, cloning ancestors
|
|
181
|
+
for (var i = 0; i < segments.length - 1; i++) {
|
|
182
|
+
var currentSegment = segments[i];
|
|
183
|
+
var nextSegment = segments[i + 1];
|
|
184
|
+
var child = pointer[currentSegment];
|
|
185
|
+
if (Array.isArray(child)) {
|
|
186
|
+
child = options.clone ? child.slice() : child;
|
|
187
|
+
} else if (isPlainObject(child)) {
|
|
188
|
+
child = options.clone ? _objectSpread2({}, child) : child;
|
|
189
|
+
} else {
|
|
190
|
+
child = typeof nextSegment === 'number' || nextSegment === '' ? [] : {};
|
|
225
191
|
}
|
|
192
|
+
pointer[currentSegment] = child;
|
|
193
|
+
pointer = child;
|
|
226
194
|
}
|
|
227
|
-
return true;
|
|
228
|
-
}
|
|
229
195
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
return false;
|
|
196
|
+
// 4) final set or push
|
|
197
|
+
var last = segments[segments.length - 1];
|
|
198
|
+
var oldValue = pointer[last];
|
|
199
|
+
var newValue = typeof valueOrFn === 'function' ? valueOrFn(oldValue) : valueOrFn;
|
|
200
|
+
if (last === '') {
|
|
201
|
+
if (!Array.isArray(pointer)) {
|
|
202
|
+
throw new Error("Cannot push to non-array at \"".concat(pathOrSegments, "\""));
|
|
238
203
|
}
|
|
204
|
+
pointer.push(newValue);
|
|
205
|
+
} else {
|
|
206
|
+
pointer[last] = newValue;
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
} catch (err) {
|
|
210
|
+
if (options !== null && options !== void 0 && options.silent) {
|
|
211
|
+
return target;
|
|
239
212
|
}
|
|
240
|
-
|
|
213
|
+
throw err;
|
|
241
214
|
}
|
|
242
|
-
return false;
|
|
243
215
|
}
|
|
244
216
|
|
|
245
217
|
/**
|
|
246
|
-
*
|
|
247
|
-
* It may contains JSON primitives if the value is updated based on a form intent.
|
|
248
|
-
*/
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* The data of a form submission.
|
|
218
|
+
* Retrive the value from a target object by following the path segments.
|
|
252
219
|
*/
|
|
220
|
+
function getValueAtPath(target, pathOrSegments) {
|
|
221
|
+
var pointer = target;
|
|
222
|
+
var segments = typeof pathOrSegments === 'string' ? getPathSegments(pathOrSegments) : pathOrSegments;
|
|
223
|
+
for (var segment of segments) {
|
|
224
|
+
if (segment === '') {
|
|
225
|
+
throw new Error("Cannot access empty segment \"[]\" in \"".concat(pathOrSegments, "\""));
|
|
226
|
+
}
|
|
227
|
+
if (pointer == null || !Object.prototype.hasOwnProperty.call(pointer, segment)) {
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
pointer = pointer[segment];
|
|
231
|
+
}
|
|
232
|
+
return pointer;
|
|
233
|
+
}
|
|
253
234
|
|
|
254
235
|
/**
|
|
255
236
|
* Parse `FormData` or `URLSearchParams` into a submission object.
|
|
256
237
|
* This function structures the form values based on the naming convention.
|
|
257
|
-
* It also includes all the field names and the intent
|
|
238
|
+
* It also includes all the field names and extracts the intent from the submission.
|
|
258
239
|
*
|
|
240
|
+
* @see https://conform.guide/api/react/future/parseSubmission
|
|
259
241
|
* @example
|
|
260
242
|
* ```ts
|
|
261
243
|
* const formData = new FormData();
|
|
@@ -265,7 +247,7 @@ function deepEqual(left, right) {
|
|
|
265
247
|
*
|
|
266
248
|
* parseSubmission(formData)
|
|
267
249
|
* // {
|
|
268
|
-
* //
|
|
250
|
+
* // payload: { email: 'test@example.com', password: 'secret' },
|
|
269
251
|
* // fields: ['email', 'password'],
|
|
270
252
|
* // intent: null,
|
|
271
253
|
* // }
|
|
@@ -274,7 +256,7 @@ function deepEqual(left, right) {
|
|
|
274
256
|
* formData.append('intent', 'login');
|
|
275
257
|
* parseSubmission(formData, { intentName: 'intent' })
|
|
276
258
|
* // {
|
|
277
|
-
* //
|
|
259
|
+
* // payload: { email: 'test@example.com', password: 'secret' },
|
|
278
260
|
* // fields: ['email', 'password'],
|
|
279
261
|
* // intent: 'login',
|
|
280
262
|
* // }
|
|
@@ -282,22 +264,21 @@ function deepEqual(left, right) {
|
|
|
282
264
|
*/
|
|
283
265
|
function parseSubmission(formData, options) {
|
|
284
266
|
var _options$intentName;
|
|
285
|
-
var intentName = (_options$intentName = options === null || options === void 0 ? void 0 : options.intentName) !== null && _options$intentName !== void 0 ? _options$intentName :
|
|
267
|
+
var intentName = (_options$intentName = options === null || options === void 0 ? void 0 : options.intentName) !== null && _options$intentName !== void 0 ? _options$intentName : DEFAULT_INTENT_NAME;
|
|
286
268
|
var submission = {
|
|
287
|
-
|
|
269
|
+
payload: {},
|
|
288
270
|
fields: [],
|
|
289
271
|
intent: null
|
|
290
272
|
};
|
|
291
|
-
var
|
|
273
|
+
for (var _name of new Set(formData.keys())) {
|
|
292
274
|
var _options$skipEntry;
|
|
293
275
|
if (_name !== intentName && !(options !== null && options !== void 0 && (_options$skipEntry = options.skipEntry) !== null && _options$skipEntry !== void 0 && _options$skipEntry.call(options, _name))) {
|
|
294
|
-
var
|
|
295
|
-
|
|
276
|
+
var _value = formData.getAll(_name);
|
|
277
|
+
setValueAtPath(submission.payload, _name, _value.length > 1 ? _value : _value[0], {
|
|
278
|
+
silent: true // Avoid errors if the path is invalid
|
|
279
|
+
});
|
|
296
280
|
submission.fields.push(_name);
|
|
297
281
|
}
|
|
298
|
-
};
|
|
299
|
-
for (var _name of new Set(formData.keys())) {
|
|
300
|
-
_loop();
|
|
301
282
|
}
|
|
302
283
|
if (intentName) {
|
|
303
284
|
// We take the first value of the intent field if it exists.
|
|
@@ -308,17 +289,63 @@ function parseSubmission(formData, options) {
|
|
|
308
289
|
}
|
|
309
290
|
return submission;
|
|
310
291
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Creates a SubmissionResult object from a submission, adding validation results and intended values.
|
|
295
|
+
* This function will remove all files in the submission payload by default since
|
|
296
|
+
* file inputs cannot be initialized with files.
|
|
297
|
+
* You can specify `keepFiles: true` to keep the files if needed.
|
|
298
|
+
*
|
|
299
|
+
* @see https://conform.guide/api/react/future/report
|
|
300
|
+
* @example
|
|
301
|
+
* ```ts
|
|
302
|
+
* // Report the submission with the field errors
|
|
303
|
+
* report(submission, {
|
|
304
|
+
* error: {
|
|
305
|
+
* fieldErrors: {
|
|
306
|
+
* email: ['Invalid email format'],
|
|
307
|
+
* password: ['Password is required'],
|
|
308
|
+
* },
|
|
309
|
+
* })
|
|
310
|
+
*
|
|
311
|
+
* // Report the submission with a form error
|
|
312
|
+
* report(submission, {
|
|
313
|
+
* error: {
|
|
314
|
+
* formErrors: ['Invalid credentials'],
|
|
315
|
+
* },
|
|
316
|
+
* })
|
|
317
|
+
*
|
|
318
|
+
* // Reset the form
|
|
319
|
+
* report(submission, {
|
|
320
|
+
* reset: true,
|
|
321
|
+
* })
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
|
|
325
|
+
function report(submission) {
|
|
326
|
+
var _options$error$formEr, _options$error$fieldE;
|
|
327
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
328
|
+
var intendedValue = options.reset ? null : typeof options.intendedValue === 'undefined' || submission.payload === options.intendedValue ? undefined : options.intendedValue && !options.keepFiles ? stripFiles(options.intendedValue) : options.intendedValue;
|
|
329
|
+
var error = !options.error ? options.error : {
|
|
330
|
+
formErrors: (_options$error$formEr = options.error.formErrors) !== null && _options$error$formEr !== void 0 ? _options$error$formEr : [],
|
|
331
|
+
fieldErrors: (_options$error$fieldE = options.error.fieldErrors) !== null && _options$error$fieldE !== void 0 ? _options$error$fieldE : {}
|
|
332
|
+
};
|
|
333
|
+
if (options.hideFields) {
|
|
334
|
+
for (var _name2 of options.hideFields) {
|
|
335
|
+
var path = getPathSegments(_name2);
|
|
336
|
+
setValueAtPath(submission.payload, path, undefined);
|
|
337
|
+
if (intendedValue) {
|
|
338
|
+
setValueAtPath(intendedValue, path, undefined);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
320
341
|
}
|
|
321
|
-
return
|
|
342
|
+
return {
|
|
343
|
+
submission: options.keepFiles ? submission : _objectSpread2(_objectSpread2({}, submission), {}, {
|
|
344
|
+
payload: stripFiles(submission.payload)
|
|
345
|
+
}),
|
|
346
|
+
intendedValue,
|
|
347
|
+
error
|
|
348
|
+
};
|
|
322
349
|
}
|
|
323
350
|
|
|
324
351
|
/**
|
|
@@ -349,17 +376,37 @@ function isDirty(
|
|
|
349
376
|
* - A plain object that was parsed from form data (i.e. `submission.payload`)
|
|
350
377
|
*/
|
|
351
378
|
formData, options) {
|
|
352
|
-
var _options$serialize;
|
|
353
379
|
if (!formData) {
|
|
354
380
|
return;
|
|
355
381
|
}
|
|
356
382
|
var formValue = formData instanceof FormData || formData instanceof URLSearchParams ? parseSubmission(formData, {
|
|
357
383
|
intentName: options === null || options === void 0 ? void 0 : options.intentName,
|
|
358
384
|
skipEntry: options === null || options === void 0 ? void 0 : options.skipEntry
|
|
359
|
-
}).
|
|
385
|
+
}).payload : formData;
|
|
360
386
|
var defaultValue = options === null || options === void 0 ? void 0 : options.defaultValue;
|
|
361
|
-
var
|
|
362
|
-
|
|
387
|
+
var serializeData = value => {
|
|
388
|
+
if (options !== null && options !== void 0 && options.serialize) {
|
|
389
|
+
return options.serialize(value, serialize);
|
|
390
|
+
}
|
|
391
|
+
return serialize(value);
|
|
392
|
+
};
|
|
393
|
+
function normalize(data) {
|
|
394
|
+
var _serializeData;
|
|
395
|
+
var value = (_serializeData = serializeData(data)) !== null && _serializeData !== void 0 ? _serializeData : data;
|
|
396
|
+
|
|
397
|
+
// Removes empty strings, so that bpth empty string and undefined are treated as the same
|
|
398
|
+
if (value === '') {
|
|
399
|
+
return undefined;
|
|
400
|
+
}
|
|
401
|
+
if (isGlobalInstance(value, 'File')) {
|
|
402
|
+
// Remove empty File as well, which happens if no File was selected
|
|
403
|
+
if (value.name === '' && value.size === 0) {
|
|
404
|
+
return undefined;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// If the value is a File, no need to serialize it
|
|
408
|
+
return value;
|
|
409
|
+
}
|
|
363
410
|
if (Array.isArray(value)) {
|
|
364
411
|
if (value.length === 0) {
|
|
365
412
|
return undefined;
|
|
@@ -384,24 +431,80 @@ formData, options) {
|
|
|
384
431
|
}
|
|
385
432
|
return Object.fromEntries(entries);
|
|
386
433
|
}
|
|
434
|
+
return value;
|
|
435
|
+
}
|
|
436
|
+
return !deepEqual(normalize(formValue), normalize(defaultValue));
|
|
437
|
+
}
|
|
387
438
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
439
|
+
/**
|
|
440
|
+
* Convert an unknown value into something acceptable for HTML form submission.
|
|
441
|
+
* Returns `undefined` when the value cannot be represented in form data.
|
|
442
|
+
*
|
|
443
|
+
* Input -> Output:
|
|
444
|
+
* - string -> string
|
|
445
|
+
* - null -> '' (empty string)
|
|
446
|
+
* - boolean -> 'on' | '' (checked semantics)
|
|
447
|
+
* - number | bigint -> value.toString()
|
|
448
|
+
* - Date -> value.toISOString()
|
|
449
|
+
* - File -> File
|
|
450
|
+
* - FileList -> File[]
|
|
451
|
+
* - Array -> string[] or File[] if all items serialize to the same kind; otherwise undefined
|
|
452
|
+
* - anything else -> undefined
|
|
453
|
+
*/
|
|
454
|
+
function serialize(value) {
|
|
455
|
+
function serializePrimitive(value) {
|
|
456
|
+
if (typeof value === 'string') {
|
|
457
|
+
return value;
|
|
391
458
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
if (typeof value === 'string' && value === '') {
|
|
395
|
-
return undefined;
|
|
459
|
+
if (value === null) {
|
|
460
|
+
return '';
|
|
396
461
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
462
|
+
if (typeof value === 'boolean') {
|
|
463
|
+
return value ? 'on' : '';
|
|
464
|
+
}
|
|
465
|
+
if (typeof value === 'number' || typeof value === 'bigint') {
|
|
466
|
+
return value.toString();
|
|
467
|
+
}
|
|
468
|
+
if (value instanceof Date) {
|
|
469
|
+
return value.toISOString();
|
|
470
|
+
}
|
|
471
|
+
if (isGlobalInstance(value, 'File')) {
|
|
472
|
+
return value;
|
|
401
473
|
}
|
|
402
|
-
return serialize(value, defaultSerialize);
|
|
403
474
|
}
|
|
404
|
-
|
|
475
|
+
if (Array.isArray(value)) {
|
|
476
|
+
var _options = [];
|
|
477
|
+
var files = [];
|
|
478
|
+
for (var item of value) {
|
|
479
|
+
var serialized = serializePrimitive(item);
|
|
480
|
+
if (typeof serialized === 'undefined') {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
if (typeof serialized === 'string') {
|
|
484
|
+
if (files.length > 0) {
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
_options.push(serialized);
|
|
488
|
+
} else {
|
|
489
|
+
if (_options.length > 0) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
files.push(serialized);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (_options.length === value.length) {
|
|
496
|
+
return _options;
|
|
497
|
+
}
|
|
498
|
+
if (files.length === value.length) {
|
|
499
|
+
return files;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// If not all items are strings or files, return nothing
|
|
503
|
+
}
|
|
504
|
+
if (isGlobalInstance(value, 'FileList')) {
|
|
505
|
+
return Array.from(value);
|
|
506
|
+
}
|
|
507
|
+
return serializePrimitive(value);
|
|
405
508
|
}
|
|
406
509
|
|
|
407
|
-
export {
|
|
510
|
+
export { DEFAULT_INTENT_NAME, appendPathSegment, formatPathSegments, getFormData, getPathSegments, getRelativePath, getValueAtPath, isDirty, isPrefix, parseSubmission, report, serialize, setValueAtPath };
|