@conform-to/dom
Version:
A set of opinionated helpers built on top of the Constraint Validation API
229 lines (212 loc) • 6.84 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
/**
* Construct a form data with the submitter value.
* It utilizes the submitter argument on the FormData constructor from modern browsers
* with fallback to append the submitter value in case it is not unsupported.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData#parameters
*/
function getFormData(form, submitter) {
var payload = new FormData(form, submitter);
if (submitter && submitter.type === 'submit' && submitter.name !== '') {
var entries = payload.getAll(submitter.name);
// This assumes the submitter value to be always unique, which should be fine in most cases
if (!entries.includes(submitter.value)) {
payload.append(submitter.name, submitter.value);
}
}
return payload;
}
/**
* Returns the paths from a name based on the JS syntax convention
* @example
* ```js
* const paths = getPaths('todos[0].content'); // ['todos', 0, 'content']
* ```
*/
function getPaths(name) {
if (!name) {
return [];
}
return name.split(/\.|(\[\d*\])/).reduce((result, segment) => {
if (typeof segment !== 'undefined' && segment !== '' && segment !== '__proto__' && segment !== 'constructor' && segment !== 'prototype') {
if (segment.startsWith('[') && segment.endsWith(']')) {
var index = segment.slice(1, -1);
result.push(Number(index));
} else {
result.push(segment);
}
}
return result;
}, []);
}
/**
* Returns a formatted name from the paths based on the JS syntax convention
* @example
* ```js
* const name = formatPaths(['todos', 0, 'content']); // "todos[0].content"
* ```
*/
function formatPaths(paths) {
return paths.reduce((name, path) => {
if (typeof path === 'number') {
return "".concat(name, "[").concat(Number.isNaN(path) ? '' : path, "]");
}
if (name === '' || path === '') {
return [name, path].join('');
}
return [name, path].join('.');
}, '');
}
/**
* Format based on a prefix and a path
*/
function formatName(prefix, path) {
return typeof path !== 'undefined' ? formatPaths([...getPaths(prefix), path]) : prefix !== null && prefix !== void 0 ? prefix : '';
}
/**
* Check if a name match the prefix paths
*/
function isPrefix(name, prefix) {
var paths = getPaths(name);
var prefixPaths = getPaths(prefix);
return paths.length >= prefixPaths.length && prefixPaths.every((path, index) => paths[index] === path);
}
/**
* Compare the parent and child paths to get the relative paths
* Returns null if the child paths do not start with the parent paths
*/
function getChildPaths(parentNameOrPaths, childName) {
var parentPaths = typeof parentNameOrPaths === 'string' ? getPaths(parentNameOrPaths) : parentNameOrPaths;
var childPaths = getPaths(childName);
if (childPaths.length >= parentPaths.length && parentPaths.every((path, index) => childPaths[index] === path)) {
return childPaths.slice(parentPaths.length);
}
return null;
}
/**
* Assign a value to a target object by following the paths
*/
function setValue(target, name, valueFn) {
var paths = getPaths(name);
var length = paths.length;
var lastIndex = length - 1;
var index = -1;
var pointer = target;
while (pointer != null && ++index < length) {
var key = paths[index];
var nextKey = paths[index + 1];
var newValue = index != lastIndex ? Object.prototype.hasOwnProperty.call(pointer, key) && pointer[key] !== null ? pointer[key] : typeof nextKey === 'number' ? [] : {} : valueFn(pointer[key]);
pointer[key] = newValue;
pointer = pointer[key];
}
}
/**
* Retrive the value from a target object by following the paths
*/
function getValue(target, name) {
var pointer = target;
for (var path of getPaths(name)) {
if (typeof pointer === 'undefined' || pointer == null) {
break;
}
if (!Object.prototype.hasOwnProperty.call(pointer, path)) {
return;
}
if (isPlainObject(pointer) && typeof path === 'string') {
pointer = pointer[path];
} else if (Array.isArray(pointer) && typeof path === 'number') {
pointer = pointer[path];
} else {
return;
}
}
return pointer;
}
/**
* Check if the value is a plain object
*/
function isPlainObject(obj) {
return !!obj && obj.constructor === Object && Object.getPrototypeOf(obj) === Object.prototype;
}
/**
* Check if the value is a File
*/
function isFile(obj) {
// Skip checking if File is not defined
if (typeof File === 'undefined') {
return false;
}
return obj instanceof File;
}
/**
* Normalize value by removing empty object or array, empty string and null values
*/
function normalize(value) {
var acceptFile = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
if (isPlainObject(value)) {
var obj = Object.keys(value).sort().reduce((result, key) => {
var data = normalize(value[key], acceptFile);
if (typeof data !== 'undefined') {
result[key] = data;
}
return result;
}, {});
if (Object.keys(obj).length === 0) {
return;
}
return obj;
}
if (Array.isArray(value)) {
if (value.length === 0) {
return undefined;
}
return value.map(item => normalize(item, acceptFile));
}
if (typeof value === 'string' && value === '' || value === null || isFile(value) && (!acceptFile || value.size === 0)) {
return;
}
return value;
}
/**
* Flatten a tree into a dictionary
*/
function flatten(data) {
var _options$resolve;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var result = {};
var resolve = (_options$resolve = options.resolve) !== null && _options$resolve !== void 0 ? _options$resolve : data => data;
function process(data, prefix) {
var value = normalize(resolve(data));
if (typeof value !== 'undefined') {
result[prefix] = value;
}
if (Array.isArray(data)) {
for (var i = 0; i < data.length; i++) {
process(data[i], "".concat(prefix, "[").concat(i, "]"));
}
} else if (isPlainObject(data)) {
for (var [key, _value] of Object.entries(data)) {
process(_value, prefix ? "".concat(prefix, ".").concat(key) : key);
}
}
}
if (data) {
var _options$prefix;
process(data, (_options$prefix = options.prefix) !== null && _options$prefix !== void 0 ? _options$prefix : '');
}
return result;
}
exports.flatten = flatten;
exports.formatName = formatName;
exports.formatPaths = formatPaths;
exports.getChildPaths = getChildPaths;
exports.getFormData = getFormData;
exports.getPaths = getPaths;
exports.getValue = getValue;
exports.isFile = isFile;
exports.isPlainObject = isPlainObject;
exports.isPrefix = isPrefix;
exports.normalize = normalize;
exports.setValue = setValue;