Skip to content

Commit

Permalink
fixes #11
Browse files Browse the repository at this point in the history
minor optimizations
  • Loading branch information
jonschlinkert committed Sep 12, 2021
1 parent 7cf8073 commit cdc7446
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 52 deletions.
103 changes: 53 additions & 50 deletions index.js
Expand Up @@ -7,27 +7,29 @@

'use strict';

const { deleteProperty } = Reflect;
const isPrimitive = require('is-primitive');
const isPlainObject = require('is-plain-object');

const isObject = val => {
return (typeof val === 'object' && val !== null) || typeof val === 'function';
const isObject = value => {
return (typeof value === 'object' && value !== null) || typeof value === 'function';
};

const isUnsafeKey = key => {
return key === '__proto__' || key === 'constructor' || key === 'prototype';
};

const validateKey = key => {
if (typeof key !== 'string' && typeof key !== 'number') {
key = String(key);
if (!isPrimitive(key)) {
throw new TypeError('Object keys must be strings or symbols');
}

if (isUnsafeKey(key)) {
throw new Error(`Cannot set unsafe key: "${key}"`);
}
};

const toString = input => {
const toStringKey = input => {
return Array.isArray(input) ? input.flat().map(String).join(',') : input;
};

Expand All @@ -43,71 +45,74 @@ const createMemoKey = (input, options) => {
};

const memoize = (input, options, fn) => {
const key = toString(options ? createMemoKey(input, options) : input);
const key = toStringKey(options ? createMemoKey(input, options) : input);
validateKey(key);

const val = setValue.cache.get(key) || fn();
setValue.cache.set(key, val);
return val;
const value = setValue.cache.get(key) || fn();
setValue.cache.set(key, value);
return value;
};

const isNumber = value => {
if (value.trim() !== '') {
const number = Number(value);
return { is: Number.isInteger(number), number };
}
return { is: false };
};

const splitString = (input, options) => {
const opts = options || {};
const sep = opts.separator || '.';
const preserve = sep === '/' ? false : opts.preservePaths;
const splitString = (input, options = {}) => {
const sep = options.separator || '.';
const preserve = sep === '/' ? false : options.preservePaths;

if (typeof input === 'symbol') {
if (typeof input === 'string' && preserve !== false && /\//.test(input)) {
return [input];
}

if (typeof opts.split === 'function') {
return opts.split(input);
}
const parts = [];
let part = '';

const keys = Array.isArray(input) ? input : input.split(sep);

if (typeof input === 'string' && preserve !== false && /\//.test(input)) {
return [input];
}
const push = part => {
let number;
if (part.trim() !== '' && Number.isInteger((number = Number(part)))) {
parts.push(number);
} else {
parts.push(part);
}
};

for (let i = 0; i < keys.length; i++) {
if (typeof keys[i] !== 'string') break;
const { is, number } = isNumber(keys[i]);
for (let i = 0; i < input.length; i++) {
const value = input[i];

if (is) {
keys[i] = number;
if (value === '\\') {
part += input[++i];
continue;
}

while (keys[i] && i < keys.length && keys[i].endsWith('\\') && typeof keys[i + 1] === 'string') {
keys[i] = keys[i].slice(0, -1) + sep + keys.splice(i + 1, 1);
if (value === sep) {
push(part);
part = '';
continue;
}

part += value;
}

if (part) {
push(part);
}

return keys;
return parts;
};

const split = (input, options) => {
if (options && typeof options.split === 'function') return options.split(input);
if (typeof input === 'symbol') return [input];
if (Array.isArray(input)) return input;
return memoize(input, options, () => splitString(input, options));
};

const setProp = (obj, prop, value, options) => {
const assignProp = (obj, prop, value, options) => {
validateKey(prop);

// Delete property when "value" is undefined
if (value === undefined) {
delete obj[prop];
deleteProperty(obj, prop);

} else if (options && options.merge) {
const merge = options.merge === true ? Object.assign : options.merge;
const merge = options.merge === 'function' ? options.merge : Object.assign;

// Only merge plain objects
if (merge && isPlainObject(obj[prop]) && isPlainObject(value)) {
Expand All @@ -123,28 +128,25 @@ const setProp = (obj, prop, value, options) => {
return obj;
};

const setValue = (obj, path, value, options) => {
if (!path) return obj;
if (!isObject(obj)) return obj;
const setValue = (target, path, value, options) => {
if (!path || !isObject(target)) return target;

const keys = split(path, options);
const len = keys.length;
const target = obj;
let obj = target;

for (let i = 0; i < len; i++) {
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const next = keys[i + 1];

validateKey(key);

if (next === undefined) {
setProp(obj, key, value, options);
assignProp(obj, key, value, options);
break;
}

if (typeof next === 'number' && !Array.isArray(obj[key])) {
obj[key] = [];
obj = obj[key];
obj = obj[key] = [];
continue;
}

Expand All @@ -158,6 +160,7 @@ const setValue = (obj, path, value, options) => {
return target;
};

setValue.split = split;
setValue.cache = new Map();
setValue.clear = () => {
setValue.cache = new Map();
Expand Down
5 changes: 3 additions & 2 deletions package.json
Expand Up @@ -28,11 +28,12 @@
"test": "mocha"
},
"dependencies": {
"is-plain-object": "^2.0.4"
"is-plain-object": "^2.0.4",
"is-primitive": "^3.0.1"
},
"devDependencies": {
"gulp-format-md": "^2.0.0",
"mocha": "^8.3.2",
"mocha": "^9.1.1",
"split-string": "^6.1.0"
},
"keywords": [
Expand Down

0 comments on commit cdc7446

Please sign in to comment.