From a443311ca20b7887d715393f0bd66d402f55b5b8 Mon Sep 17 00:00:00 2001 From: Branden Horiuchi Date: Sat, 27 Jan 2018 21:17:54 -0800 Subject: [PATCH] reverted back to old toPath and vueSet code --- index.js | 77 +++++++++++++----------------------- package.json | 2 +- src/index.js | 87 ++++++++++++++++++----------------------- test/issue12.html | 97 ++++++++++++++++++++++++++++++++++++++++++++++ vue-deepset.js | 77 +++++++++++++----------------------- vue-deepset.min.js | 2 +- 6 files changed, 192 insertions(+), 150 deletions(-) create mode 100644 test/issue12.html diff --git a/index.js b/index.js index 78f4903..c0a8931 100644 --- a/index.js +++ b/index.js @@ -23,39 +23,25 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de var invalidKey = /^\d|[^a-zA-Z0-9_]/gm; var intKey = /^\d+$/; -var charCodeOfDot = '.'.charCodeAt(0); -var reEscapeChar = /\\(\\)?/g; -var rePropName = RegExp( -// Match anything that isn't a dot or bracket. -'[^.[\\]]+' + '|' + -// Or match property names within brackets. -'\\[(?:' + -// Match a non-string expression. -'([^"\'].*)' + '|' + -// Or match strings (supports escaping characters). -'(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' + ')\\]' + '|' + -// Or match "" as the space between consecutive dots or empty brackets. -'(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))', 'g'); - -// modified from lodash - /~https://github.com/lodash/lodash -function toPath(string) { - if (Array.isArray(string)) { - return string; - } - var result = []; - if (string.charCodeAt(0) === charCodeOfDot) { - result.push(''); - } - string.replace(rePropName, function (match, expression, quote, subString) { - var key = match; - if (quote) { - key = subString.replace(reEscapeChar, '$1'); - } else if (expression) { - key = expression.trim(); - } - result.push(key); + +function isNumberLike(value) { + return String(value).match(/^\d+$/); +} + +function toPath(pathString) { + if (Array.isArray(pathString)) return pathString; + if (typeof pathString === 'number') return [pathString]; + pathString = String(pathString); + + // taken from lodash - /~https://github.com/lodash/lodash + var pathRx = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(\.|\[\])(?:\4|$))/g; + var pathArray = []; + + pathString.replace(pathRx, function (match, number, quote, string) { + pathArray.push(quote ? string : number !== undefined ? Number(number) : match); + return pathArray[pathArray.length - 1]; }); - return result; + return pathArray; } function noop() {} @@ -141,7 +127,7 @@ function getPaths(object) { function _get(obj, path, defaultValue) { try { var o = obj; - var fields = Array.isArray(path) ? path : toPath(path); + var fields = toPath(path); while (fields.length) { var prop = fields.shift(); o = o[prop]; @@ -231,23 +217,16 @@ function buildVuexModel(vm, vuexPath, options) { return model; } -function vueSet(object, path, value) { - try { - var parts = toPath(path); - var obj = object; - while (parts.length) { - var key = parts.shift(); - if (!parts.length) { - _vue2.default.set(obj, key, value); - } else if (!hasOwnProperty(obj, key) || obj[key] === null) { - _vue2.default.set(obj, key, typeof key === 'number' ? [] : {}); - } - obj = obj[key]; - } - return object; - } catch (err) { - throw deepsetError('vueSet unable to set object (' + err.message + ')'); +function vueSet(obj, path, value) { + var fields = Array.isArray(path) ? path : toPath(path); + var prop = fields.shift(); + + if (!fields.length) return _vue2.default.set(obj, prop, value); + if (!hasOwnProperty(obj, prop) || obj[prop] === null) { + var objVal = fields.length >= 1 && isNumberLike(fields[0]) ? [] : {}; + _vue2.default.set(obj, prop, objVal); } + vueSet(obj[prop], fields, value); } function vuexSet(path, value) { diff --git a/package.json b/package.json index 461ec93..5a56bf2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-deepset", - "version": "0.6.2", + "version": "0.6.3", "description": "Deep set Vue.js objects", "main": "index.js", "scripts": { diff --git a/src/index.js b/src/index.js index abe6db1..2cb0fab 100644 --- a/src/index.js +++ b/src/index.js @@ -2,41 +2,31 @@ import Vue from 'vue' const invalidKey = /^\d|[^a-zA-Z0-9_]/gm const intKey = /^\d+$/ -const charCodeOfDot = '.'.charCodeAt(0) -const reEscapeChar = /\\(\\)?/g -const rePropName = RegExp( - // Match anything that isn't a dot or bracket. - '[^.[\\]]+' + '|' + - // Or match property names within brackets. - '\\[(?:' + - // Match a non-string expression. - '([^"\'].*)' + '|' + - // Or match strings (supports escaping characters). - '(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' + - ')\\]' + '|' + - // Or match "" as the space between consecutive dots or empty brackets. - '(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))' - , 'g') -// modified from lodash - /~https://github.com/lodash/lodash -function toPath (string) { - if (Array.isArray(string)) { - return string - } - const result = [] - if (string.charCodeAt(0) === charCodeOfDot) { - result.push('') - } - string.replace(rePropName, (match, expression, quote, subString) => { - let key = match - if (quote) { - key = subString.replace(reEscapeChar, '$1') - } else if (expression) { - key = expression.trim() - } - result.push(key) +function isNumberLike(value) { + return String(value).match(/^\d+$/); +} + +function toPath (pathString) { + if (Array.isArray(pathString)) return pathString + if (typeof pathString === 'number') return [ pathString ] + pathString = String(pathString) + + // taken from lodash - /~https://github.com/lodash/lodash + let pathRx = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(\.|\[\])(?:\4|$))/g + let pathArray = [] + + pathString.replace(pathRx, (match, number, quote, string) => { + pathArray.push( + quote + ? string + : number !== undefined + ? Number(number) + : match + ) + return pathArray[pathArray.length - 1] }) - return result + return pathArray } function noop () {} @@ -120,7 +110,7 @@ function getPaths (object, current = '', paths = []) { function get (obj, path, defaultValue) { try { let o = obj - const fields = Array.isArray(path) ? path : toPath(path) + const fields = toPath(path) while (fields.length) { const prop = fields.shift() o = o[prop] @@ -204,23 +194,20 @@ function buildVuexModel (vm, vuexPath, options) { return model } -export function vueSet (object, path, value) { - try { - const parts = toPath(path) - let obj = object - while (parts.length) { - const key = parts.shift() - if (!parts.length) { - Vue.set(obj, key, value) - } else if (!hasOwnProperty(obj, key) || obj[key] === null) { - Vue.set(obj, key, typeof key === 'number' ? [] : {}) - } - obj = obj[key] - } - return object - } catch (err) { - throw deepsetError(`vueSet unable to set object (${err.message})`) +export function vueSet (obj, path, value) { + let fields = Array.isArray(path) + ? path + : toPath(path) + let prop = fields.shift() + + if (!fields.length) return Vue.set(obj, prop, value) + if (!hasOwnProperty(obj, prop) || obj[prop] === null) { + const objVal = fields.length >= 1 && isNumberLike(fields[0]) + ? [] + : {} + Vue.set(obj, prop, objVal) } + vueSet(obj[prop], fields, value) } export function vuexSet (path, value) { diff --git a/test/issue12.html b/test/issue12.html new file mode 100644 index 0000000..bea847b --- /dev/null +++ b/test/issue12.html @@ -0,0 +1,97 @@ + + + + + Reproduce issue 12 + + + + + + +
+
+
+

Books info

+ +
+

+	
+
+ + + + + \ No newline at end of file diff --git a/vue-deepset.js b/vue-deepset.js index ade9ae6..8cd4aeb 100644 --- a/vue-deepset.js +++ b/vue-deepset.js @@ -24,39 +24,25 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de var invalidKey = /^\d|[^a-zA-Z0-9_]/gm; var intKey = /^\d+$/; -var charCodeOfDot = '.'.charCodeAt(0); -var reEscapeChar = /\\(\\)?/g; -var rePropName = RegExp( -// Match anything that isn't a dot or bracket. -'[^.[\\]]+' + '|' + -// Or match property names within brackets. -'\\[(?:' + -// Match a non-string expression. -'([^"\'].*)' + '|' + -// Or match strings (supports escaping characters). -'(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' + ')\\]' + '|' + -// Or match "" as the space between consecutive dots or empty brackets. -'(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))', 'g'); - -// modified from lodash - /~https://github.com/lodash/lodash -function toPath(string) { - if (Array.isArray(string)) { - return string; - } - var result = []; - if (string.charCodeAt(0) === charCodeOfDot) { - result.push(''); - } - string.replace(rePropName, function (match, expression, quote, subString) { - var key = match; - if (quote) { - key = subString.replace(reEscapeChar, '$1'); - } else if (expression) { - key = expression.trim(); - } - result.push(key); + +function isNumberLike(value) { + return String(value).match(/^\d+$/); +} + +function toPath(pathString) { + if (Array.isArray(pathString)) return pathString; + if (typeof pathString === 'number') return [pathString]; + pathString = String(pathString); + + // taken from lodash - /~https://github.com/lodash/lodash + var pathRx = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(\.|\[\])(?:\4|$))/g; + var pathArray = []; + + pathString.replace(pathRx, function (match, number, quote, string) { + pathArray.push(quote ? string : number !== undefined ? Number(number) : match); + return pathArray[pathArray.length - 1]; }); - return result; + return pathArray; } function noop() {} @@ -142,7 +128,7 @@ function getPaths(object) { function _get(obj, path, defaultValue) { try { var o = obj; - var fields = Array.isArray(path) ? path : toPath(path); + var fields = toPath(path); while (fields.length) { var prop = fields.shift(); o = o[prop]; @@ -232,23 +218,16 @@ function buildVuexModel(vm, vuexPath, options) { return model; } -function vueSet(object, path, value) { - try { - var parts = toPath(path); - var obj = object; - while (parts.length) { - var key = parts.shift(); - if (!parts.length) { - _vue2.default.set(obj, key, value); - } else if (!hasOwnProperty(obj, key) || obj[key] === null) { - _vue2.default.set(obj, key, typeof key === 'number' ? [] : {}); - } - obj = obj[key]; - } - return object; - } catch (err) { - throw deepsetError('vueSet unable to set object (' + err.message + ')'); +function vueSet(obj, path, value) { + var fields = Array.isArray(path) ? path : toPath(path); + var prop = fields.shift(); + + if (!fields.length) return _vue2.default.set(obj, prop, value); + if (!hasOwnProperty(obj, prop) || obj[prop] === null) { + var objVal = fields.length >= 1 && isNumberLike(fields[0]) ? [] : {}; + _vue2.default.set(obj, prop, objVal); } + vueSet(obj[prop], fields, value); } function vuexSet(path, value) { diff --git a/vue-deepset.min.js b/vue-deepset.min.js index fb95d6b..2e58ffa 100644 --- a/vue-deepset.min.js +++ b/vue-deepset.min.js @@ -1 +1 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.VueDeepSet=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o1&&arguments[1]!==undefined?arguments[1]:"";var paths=arguments.length>2&&arguments[2]!==undefined?arguments[2]:[];if(Array.isArray(object)){forEach(object,function(val,idx){pushPaths(val,(current+"."+idx).replace(/^\./,""),paths);pushPaths(val,(current+"["+idx+"]").replace(/^\./,""),paths);pushPaths(val,(current+'["'+idx+'"]').replace(/^\./,""),paths)})}else if(isObjectLike(object)){forEach(object,function(val,key){if(key.match(intKey)!==null){pushPaths(val,(current+"."+key).replace(/^\./,""),paths);pushPaths(val,(current+"["+key+"]").replace(/^\./,""),paths);pushPaths(val,(current+'["'+key+'"]').replace(/^\./,""),paths)}else if(!key.match(invalidKey)){pushPaths(val,(current+"."+key).replace(/^\./,""),paths)}pushPaths(val,(current+'["'+key+'"]').replace(/^\./,""),paths)})}return[].concat(new Set(paths))}function _get(obj,path,defaultValue){try{var o=obj;var fields=Array.isArray(path)?path:toPath(path);while(fields.length){var prop=fields.shift();o=o[prop];if(!fields.length){return o}}}catch(err){return defaultValue}return defaultValue}function getProxy(vm,base,options){noop(options);var isVuex=typeof base==="string";var object=isVuex?_get(vm.$store.state,base):base;return new Proxy(object,{get:function get(target,property){return _get(target,property)},set:function set(target,property,value){isVuex?vuexSet.call(vm,pathJoin(base,property),value):vueSet(target,property,value);return true},deleteProperty:function deleteProperty(){return true},enumerate:function enumerate(target){return Object.keys(target)},ownKeys:function ownKeys(target){return Object.keys(target)},has:function has(target,property){return true},defineProperty:function defineProperty(target){return target},getOwnPropertyDescriptor:function getOwnPropertyDescriptor(target,property){return{value:_get(target,property),writable:false,enumerable:true,configurable:true}}})}function buildVueModel(vm,object,options){var model={};forEach(getPaths(object),function(path){Object.defineProperty(model,path,{configurable:true,enumerable:true,get:function get(){return _get(object,path)},set:function set(value){return vueSet(object,path,value)}})});return model}function buildVuexModel(vm,vuexPath,options){var model=Object.create(null);var object=_get(vm.$store.state,vuexPath);var paths=getPaths(object);forEach(paths,function(path){var propPath=pathJoin(vuexPath,path);Object.defineProperty(model,path,{configurable:true,enumerable:true,get:function get(){return _get(vm.$store.state,propPath)},set:function set(value){return vuexSet.call(vm,propPath,value)}})});return model}function vueSet(object,path,value){try{var parts=toPath(path);var obj=object;while(parts.length){var key=parts.shift();if(!parts.length){_vue2.default.set(obj,key,value)}else if(!hasOwnProperty(obj,key)||obj[key]===null){_vue2.default.set(obj,key,typeof key==="number"?[]:{})}obj=obj[key]}return object}catch(err){throw deepsetError("vueSet unable to set object ("+err.message+")")}}function vuexSet(path,value){if(!isObjectLike(this.$store)){throw deepsetError("could not find vuex store object on instance")}var method=this.$store.commit?"commit":"dispatch";this.$store[method]("VUEX_DEEP_SET",{path:path,value:value})}function VUEX_DEEP_SET(state,_ref){var path=_ref.path,value=_ref.value;vueSet(state,path,value)}function extendMutation(){var mutations=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};return Object.assign(mutations,{VUEX_DEEP_SET:VUEX_DEEP_SET})}function vueModel(object,options){var opts=Object.assign({},options);if(!isObjectLike(object)){throw deepsetError("invalid object specified for vue model")}else if(opts.useProxy===false||typeof Proxy==="undefined"){return buildVueModel(this,object,opts)}return getProxy(this,object,opts)}function vuexModel(vuexPath,options){var opts=Object.assign({},options);if(typeof vuexPath!=="string"||vuexPath===""){throw deepsetError("invalid vuex path string")}else if(!isObjectLike(this.$store)||!isObjectLike(this.$store.state)){throw deepsetError("no vuex state found")}else if(!has(this.$store.state,vuexPath)){throw deepsetError('cannot find path "'+vuexPath+'" in Vuex store')}else if(opts.useProxy===false||typeof Proxy==="undefined"){return buildVuexModel(this,vuexPath,opts)}return getProxy(this,vuexPath,opts)}function deepModel(base,options){return typeof base==="string"?vuexModel.call(this,base,options):vueModel.call(this,base,options)}function install(VueInstance){VueInstance.prototype.$deepModel=deepModel;VueInstance.prototype.$vueSet=vueSet;VueInstance.prototype.$vuexSet=vuexSet}},{}]},{},[1])(1)}); +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.VueDeepSet=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o1&&arguments[1]!==undefined?arguments[1]:"";var paths=arguments.length>2&&arguments[2]!==undefined?arguments[2]:[];if(Array.isArray(object)){forEach(object,function(val,idx){pushPaths(val,(current+"."+idx).replace(/^\./,""),paths);pushPaths(val,(current+"["+idx+"]").replace(/^\./,""),paths);pushPaths(val,(current+'["'+idx+'"]').replace(/^\./,""),paths)})}else if(isObjectLike(object)){forEach(object,function(val,key){if(key.match(intKey)!==null){pushPaths(val,(current+"."+key).replace(/^\./,""),paths);pushPaths(val,(current+"["+key+"]").replace(/^\./,""),paths);pushPaths(val,(current+'["'+key+'"]').replace(/^\./,""),paths)}else if(!key.match(invalidKey)){pushPaths(val,(current+"."+key).replace(/^\./,""),paths)}pushPaths(val,(current+'["'+key+'"]').replace(/^\./,""),paths)})}return[].concat(new Set(paths))}function _get(obj,path,defaultValue){try{var o=obj;var fields=toPath(path);while(fields.length){var prop=fields.shift();o=o[prop];if(!fields.length){return o}}}catch(err){return defaultValue}return defaultValue}function getProxy(vm,base,options){noop(options);var isVuex=typeof base==="string";var object=isVuex?_get(vm.$store.state,base):base;return new Proxy(object,{get:function get(target,property){return _get(target,property)},set:function set(target,property,value){isVuex?vuexSet.call(vm,pathJoin(base,property),value):vueSet(target,property,value);return true},deleteProperty:function deleteProperty(){return true},enumerate:function enumerate(target){return Object.keys(target)},ownKeys:function ownKeys(target){return Object.keys(target)},has:function has(target,property){return true},defineProperty:function defineProperty(target){return target},getOwnPropertyDescriptor:function getOwnPropertyDescriptor(target,property){return{value:_get(target,property),writable:false,enumerable:true,configurable:true}}})}function buildVueModel(vm,object,options){var model={};forEach(getPaths(object),function(path){Object.defineProperty(model,path,{configurable:true,enumerable:true,get:function get(){return _get(object,path)},set:function set(value){return vueSet(object,path,value)}})});return model}function buildVuexModel(vm,vuexPath,options){var model=Object.create(null);var object=_get(vm.$store.state,vuexPath);var paths=getPaths(object);forEach(paths,function(path){var propPath=pathJoin(vuexPath,path);Object.defineProperty(model,path,{configurable:true,enumerable:true,get:function get(){return _get(vm.$store.state,propPath)},set:function set(value){return vuexSet.call(vm,propPath,value)}})});return model}function vueSet(obj,path,value){var fields=Array.isArray(path)?path:toPath(path);var prop=fields.shift();if(!fields.length)return _vue2.default.set(obj,prop,value);if(!hasOwnProperty(obj,prop)||obj[prop]===null){var objVal=fields.length>=1&&isNumberLike(fields[0])?[]:{};_vue2.default.set(obj,prop,objVal)}vueSet(obj[prop],fields,value)}function vuexSet(path,value){if(!isObjectLike(this.$store)){throw deepsetError("could not find vuex store object on instance")}var method=this.$store.commit?"commit":"dispatch";this.$store[method]("VUEX_DEEP_SET",{path:path,value:value})}function VUEX_DEEP_SET(state,_ref){var path=_ref.path,value=_ref.value;vueSet(state,path,value)}function extendMutation(){var mutations=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};return Object.assign(mutations,{VUEX_DEEP_SET:VUEX_DEEP_SET})}function vueModel(object,options){var opts=Object.assign({},options);if(!isObjectLike(object)){throw deepsetError("invalid object specified for vue model")}else if(opts.useProxy===false||typeof Proxy==="undefined"){return buildVueModel(this,object,opts)}return getProxy(this,object,opts)}function vuexModel(vuexPath,options){var opts=Object.assign({},options);if(typeof vuexPath!=="string"||vuexPath===""){throw deepsetError("invalid vuex path string")}else if(!isObjectLike(this.$store)||!isObjectLike(this.$store.state)){throw deepsetError("no vuex state found")}else if(!has(this.$store.state,vuexPath)){throw deepsetError('cannot find path "'+vuexPath+'" in Vuex store')}else if(opts.useProxy===false||typeof Proxy==="undefined"){return buildVuexModel(this,vuexPath,opts)}return getProxy(this,vuexPath,opts)}function deepModel(base,options){return typeof base==="string"?vuexModel.call(this,base,options):vueModel.call(this,base,options)}function install(VueInstance){VueInstance.prototype.$deepModel=deepModel;VueInstance.prototype.$vueSet=vueSet;VueInstance.prototype.$vuexSet=vuexSet}},{}]},{},[1])(1)});