deneric2
Version:
Zero-dependency JSON-to-Entity parser with schema support
255 lines (252 loc) • 7.83 kB
JavaScript
'use strict';
// src/utils.ts
function parsePath(path) {
if (!path) return [];
const result = [];
const regex = /[^.[\]]+/g;
let match;
while ((match = regex.exec(path)) !== null) {
result.push(match[0]);
}
return result;
}
function get(obj, path) {
if (obj == null) return void 0;
const keys = parsePath(path);
let current = obj;
for (const key of keys) {
if (current == null) return void 0;
current = current[key];
}
return current;
}
function set(obj, path, value) {
if (obj == null || typeof obj !== "object") return obj;
const keys = parsePath(path);
if (keys.length === 0) return obj;
let current = obj;
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
const nextKey = keys[i + 1];
if (current[key] == null || typeof current[key] !== "object") {
current[key] = /^\d+$/.test(nextKey) ? [] : {};
}
current = current[key];
}
current[keys[keys.length - 1]] = value;
return obj;
}
function cloneDeep(value) {
if (value === null || value === void 0) return value;
if (typeof value !== "object") return value;
if (Array.isArray(value)) {
return value.map((item) => cloneDeep(item));
}
if (value instanceof Date) {
return new Date(value.getTime());
}
if (value instanceof RegExp) {
return new RegExp(value.source, value.flags);
}
const result = Object.create(Object.getPrototypeOf(value));
for (const key of Object.keys(value)) {
result[key] = cloneDeep(value[key]);
}
return result;
}
function isPlainObject(value) {
if (value === null || value === void 0) return false;
if (typeof value !== "object") return false;
if (Array.isArray(value)) return false;
const proto = Object.getPrototypeOf(value);
return proto === Object.prototype || proto === null;
}
// src/Deneric.ts
var COMMON_DATA_TYPES = [String, Number, Boolean, Array, Object];
var DENERIC_SCHEMA_PATH = "__deneric_schema";
var ComplexDataType = class {
constructor() {
this.isArray = false;
this.isMap = false;
}
};
var ArrayDataType = class extends ComplexDataType {
constructor(itemType) {
super();
this.itemType = itemType;
this.isArray = true;
}
};
var MapDataType = class extends ComplexDataType {
constructor(itemType) {
super();
this.itemType = itemType;
this.isMap = true;
}
};
var Utils = Object.freeze({
getValue(data, dataPath) {
if (!!dataPath) {
return get(data, dataPath);
}
return data;
},
getValueFromJson(data, dataType, defaultValue, strict) {
if (dataType.prototype instanceof Deneric) {
return new dataType(data);
}
if (dataType instanceof ComplexDataType) {
const complexDataType = dataType;
if (complexDataType.isArray) {
return Array.isArray(data) ? data.map((item) => Utils.getValueFromJson(item, complexDataType.itemType, Utils.getDefaultValue(complexDataType.itemType), strict)) : defaultValue;
}
if (complexDataType.isMap && isPlainObject(data)) {
return Object.keys(data).reduce((prev, key) => {
set(prev, key, Utils.getValueFromJson(Utils.getValue(data, key), complexDataType.itemType, Utils.getDefaultValue(complexDataType.itemType), strict));
return prev;
}, {});
}
return defaultValue;
}
const dataIsNil = data === null || data === void 0;
if (!COMMON_DATA_TYPES.includes(dataType)) {
return dataIsNil ? defaultValue : data;
}
if (strict) {
switch (dataType) {
case String:
return typeof data === "string" ? data : defaultValue;
case Number:
return typeof data === "number" ? data : defaultValue;
case Boolean:
return typeof data === "boolean" ? data : defaultValue;
case Array:
return Array.isArray(data) ? data : defaultValue;
case Object:
return isPlainObject(data) ? data : defaultValue;
}
}
if (!strict && !dataIsNil) {
switch (dataType) {
case String:
return String(data);
case Number:
return Number(data);
case Boolean:
return Boolean(data);
case Array:
return [].concat(data);
case Object:
return Object(data);
}
}
return defaultValue;
},
getValueFromDeneric(data, dataType, defaultValue) {
switch (dataType) {
case String:
return typeof data === "string" ? data : defaultValue;
case Number:
return typeof data === "number" ? data : defaultValue;
case Boolean:
return typeof data === "boolean" ? data : defaultValue;
case Array:
return Array.isArray(data) ? data : defaultValue;
case Object:
return isPlainObject(data) ? cloneDeep(data) : defaultValue;
}
if (dataType instanceof ComplexDataType) {
const complexDataType = dataType;
if (complexDataType.isArray) {
return Array.isArray(data) ? data.map((item) => Utils.getValueFromDeneric(item, complexDataType.itemType, Utils.getDefaultValue(complexDataType.itemType))) : defaultValue;
}
if (complexDataType.isMap) {
data = isPlainObject(data) ? data : {};
return Object.keys(data).reduce((prev, key) => {
set(prev, key, Utils.getValueFromDeneric(data[key], complexDataType.itemType, Utils.getDefaultValue(complexDataType.itemType)));
return prev;
}, {});
}
}
if (dataType.prototype instanceof Deneric) {
return data.toJson();
}
return data;
},
getDefaultValue(dataType) {
if (dataType instanceof ComplexDataType) {
const complexDataType = dataType;
if (complexDataType.isArray) {
return [];
}
if (complexDataType.isMap) {
return {};
}
}
switch (dataType) {
case String:
return "";
case Number:
return 0;
case Boolean:
return false;
case Array:
return [];
case Object:
return {};
}
return void 0;
},
setSchema(instance, schema) {
set(instance, DENERIC_SCHEMA_PATH, schema);
},
getSchema(instance) {
return get(instance, DENERIC_SCHEMA_PATH) || {};
}
});
var Deneric = class {
// private __proto__!: { schema: DenericSchema }
constructor(schema) {
if (!schema) {
throw new TypeError("Invalid schema: " + this.constructor.name);
}
Utils.setSchema(this, schema);
}
clone() {
return cloneDeep(this);
}
fromJson(data, strict = true) {
const schema = Utils.getSchema(this);
Object.keys(schema).forEach((key) => {
var _a, _b;
const [dataPath, dataType] = schema[key];
let defaultValue = (_a = get(this, key)) != null ? _a : Utils.getDefaultValue(dataType);
if (schema[key][3] === void 0) {
schema[key][3] = defaultValue;
} else {
defaultValue = (_b = schema[key][3]) != null ? _b : Utils.getDefaultValue(dataType);
}
const value = Utils.getValueFromJson(cloneDeep(Utils.getValue(data, dataPath)), dataType, defaultValue, strict);
set(this, key, value);
});
return this;
}
toJson() {
const schema = Utils.getSchema(this);
const json = {};
Object.keys(schema).forEach((key) => {
const [dataPath, dataType, jsonIgnore, defaultValueFromSchema] = schema[key];
if (!jsonIgnore) {
set(json, dataPath, Utils.getValueFromDeneric(get(this, key), dataType, defaultValueFromSchema || Utils.getDefaultValue(dataType)));
}
});
return json;
}
};
Deneric.Array = (dataType) => new ArrayDataType(dataType);
Deneric.Map = (valueDataType) => new MapDataType(valueDataType);
Deneric.Utils = Utils;
var Deneric_default = Deneric;
module.exports = Deneric_default;
//# sourceMappingURL=index.cjs.map
//# sourceMappingURL=index.cjs.map