├── .gitignore ├── .travis.yml ├── bower.json ├── index.js ├── lib └── ObjectPath.js ├── license ├── package-lock.json ├── package.json ├── readme.md └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | - "8" 5 | - "10" -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "objectpath", 3 | "author": "Mike Marcacci ", 4 | "license": "MIT", 5 | "version": "1.0.5", 6 | "description": "Parse js object paths using both dot and bracket notation. Stringify an array of properties into a valid path.", 7 | "main": "lib/ObjectPath.js", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "test", 12 | "bower_components", 13 | "tests" 14 | ], 15 | "homepage": "https://github.com/mike-marcacci/objectpath", 16 | "authors": [ 17 | "Mike Marcacci " 18 | ], 19 | "moduleType": [ 20 | "amd", 21 | "globals", 22 | "node" 23 | ], 24 | "keywords": [ 25 | "js object", 26 | "dot notation", 27 | "bracket notation", 28 | "path", 29 | "parse", 30 | "string" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/ObjectPath.js').ObjectPath; 2 | -------------------------------------------------------------------------------- /lib/ObjectPath.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | ;!function(undefined) { 4 | var regex = { 5 | "'": /\\\'/g, 6 | '"': /\\\"/g, 7 | }; 8 | 9 | var ObjectPath = { 10 | parse: function(str){ 11 | if(typeof str !== 'string'){ 12 | throw new TypeError('ObjectPath.parse must be passed a string'); 13 | } 14 | 15 | var i = 0; 16 | var parts = []; 17 | var dot, bracket, quote, closing; 18 | while (i < str.length) { 19 | dot = str.indexOf('.', i); 20 | bracket = str.indexOf('[', i); 21 | 22 | // we've reached the end 23 | if (dot === -1 && bracket === -1) { 24 | parts.push(str.slice(i, str.length)); 25 | i = str.length; 26 | } 27 | // dots 28 | else if (bracket === -1 || (dot !== -1 && dot < bracket)) { 29 | parts.push(str.slice(i, dot)); 30 | i = dot + 1; 31 | } 32 | // brackets 33 | else { 34 | if (bracket > i) { 35 | parts.push(str.slice(i, bracket)); 36 | i = bracket; 37 | } 38 | quote = str.slice(bracket + 1, bracket + 2); 39 | 40 | if (quote !== '"' && quote !== "'") { 41 | closing = str.indexOf(']', bracket); 42 | if (closing === -1) { 43 | closing = str.length; 44 | } 45 | parts.push(str.slice(i + 1, closing)); 46 | i = (str.slice(closing + 1, closing + 2) === '.') ? closing + 2 : closing + 1; 47 | } else { 48 | closing = str.indexOf(quote + ']', bracket); 49 | if (closing === -1) { 50 | closing = str.length; 51 | } 52 | while (str.slice(closing - 1, closing) === '\\' && bracket < str.length) { 53 | bracket++; 54 | closing = str.indexOf(quote + ']', bracket); 55 | } 56 | parts.push(str.slice(i + 2, closing).replace(regex[quote], quote).replace(/\\+/g, function (backslash) { 57 | return new Array(Math.ceil(backslash.length / 2) + 1).join('\\'); 58 | })); 59 | i = (str.slice(closing + 2, closing + 3) === '.') ? closing + 3 : closing + 2; 60 | } 61 | } 62 | } 63 | return parts; 64 | }, 65 | 66 | // root === true : auto calculate root; must be dot-notation friendly 67 | // root String : the string to use as root 68 | stringify: function(arr, quote, forceQuote){ 69 | if(!Array.isArray(arr)) 70 | arr = [ arr ]; 71 | 72 | quote = (quote === '"') ? '"' : "'"; 73 | var regexp = new RegExp('(\\\\|' + quote + ')', 'g'); // regex => /(\\|')/g 74 | 75 | return arr.map(function(value, key) { 76 | let property = value.toString(); 77 | if (!forceQuote && /^[A-z_]\w*$/.exec(property)) { // str with only A-z0-9_ chars will display `foo.bar` 78 | return (key !== 0) ? '.' + property : property; 79 | } else if (!forceQuote && /^\d+$/.exec(property)) { // str with only numbers will display `foo[0]` 80 | return '[' + property + ']'; 81 | } else { 82 | property = property.replace(regexp, '\\$1'); 83 | return '[' + quote + property + quote + ']'; 84 | } 85 | }).join(''); 86 | }, 87 | 88 | normalize: function(data, quote, forceQuote){ 89 | return ObjectPath.stringify(Array.isArray(data) ? data : ObjectPath.parse(data), quote, forceQuote); 90 | }, 91 | 92 | // Angular 93 | registerModule: function(angular) { 94 | angular.module('ObjectPath', []).provider('ObjectPath', function(){ 95 | this.parse = ObjectPath.parse; 96 | this.stringify = ObjectPath.stringify; 97 | this.normalize = ObjectPath.normalize; 98 | this.$get = function(){ 99 | return ObjectPath; 100 | }; 101 | }); 102 | } 103 | }; 104 | 105 | // AMD 106 | if (typeof define === 'function' && define.amd) { 107 | define(function() { 108 | return { 109 | 110 | // this is only namespaced for backwards compatibility when fixing issue #8 111 | ObjectPath: ObjectPath, 112 | 113 | // export these as top-level functions 114 | parse: ObjectPath.parse, 115 | stringify: ObjectPath.stringify, 116 | normalize: ObjectPath.normalize 117 | }; 118 | }); 119 | } 120 | 121 | // CommonJS 122 | else if (typeof exports === 'object') { 123 | exports.ObjectPath = ObjectPath; 124 | } 125 | 126 | // Browser global 127 | else { 128 | window.ObjectPath = ObjectPath; 129 | } 130 | 131 | }(); 132 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Mike Marcacci 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "objectpath", 3 | "version": "2.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "assertion-error": { 8 | "version": "1.1.0", 9 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 10 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 11 | "dev": true 12 | }, 13 | "balanced-match": { 14 | "version": "1.0.0", 15 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 16 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 17 | "dev": true 18 | }, 19 | "brace-expansion": { 20 | "version": "1.1.11", 21 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 22 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 23 | "dev": true, 24 | "requires": { 25 | "balanced-match": "^1.0.0", 26 | "concat-map": "0.0.1" 27 | } 28 | }, 29 | "browser-stdout": { 30 | "version": "1.3.1", 31 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 32 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 33 | "dev": true 34 | }, 35 | "chai": { 36 | "version": "4.2.0", 37 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", 38 | "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", 39 | "dev": true, 40 | "requires": { 41 | "assertion-error": "^1.1.0", 42 | "check-error": "^1.0.2", 43 | "deep-eql": "^3.0.1", 44 | "get-func-name": "^2.0.0", 45 | "pathval": "^1.1.0", 46 | "type-detect": "^4.0.5" 47 | } 48 | }, 49 | "check-error": { 50 | "version": "1.0.2", 51 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 52 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 53 | "dev": true 54 | }, 55 | "commander": { 56 | "version": "2.15.1", 57 | "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", 58 | "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", 59 | "dev": true 60 | }, 61 | "concat-map": { 62 | "version": "0.0.1", 63 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 64 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 65 | "dev": true 66 | }, 67 | "debug": { 68 | "version": "3.1.0", 69 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 70 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 71 | "dev": true, 72 | "requires": { 73 | "ms": "2.0.0" 74 | } 75 | }, 76 | "deep-eql": { 77 | "version": "3.0.1", 78 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 79 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 80 | "dev": true, 81 | "requires": { 82 | "type-detect": "^4.0.0" 83 | } 84 | }, 85 | "diff": { 86 | "version": "3.5.0", 87 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 88 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 89 | "dev": true 90 | }, 91 | "escape-string-regexp": { 92 | "version": "1.0.5", 93 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 94 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 95 | "dev": true 96 | }, 97 | "fs.realpath": { 98 | "version": "1.0.0", 99 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 100 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 101 | "dev": true 102 | }, 103 | "get-func-name": { 104 | "version": "2.0.0", 105 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 106 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 107 | "dev": true 108 | }, 109 | "glob": { 110 | "version": "7.1.2", 111 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 112 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 113 | "dev": true, 114 | "requires": { 115 | "fs.realpath": "^1.0.0", 116 | "inflight": "^1.0.4", 117 | "inherits": "2", 118 | "minimatch": "^3.0.4", 119 | "once": "^1.3.0", 120 | "path-is-absolute": "^1.0.0" 121 | } 122 | }, 123 | "growl": { 124 | "version": "1.10.5", 125 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 126 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 127 | "dev": true 128 | }, 129 | "has-flag": { 130 | "version": "3.0.0", 131 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 132 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 133 | "dev": true 134 | }, 135 | "he": { 136 | "version": "1.1.1", 137 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 138 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 139 | "dev": true 140 | }, 141 | "inflight": { 142 | "version": "1.0.6", 143 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 144 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 145 | "dev": true, 146 | "requires": { 147 | "once": "^1.3.0", 148 | "wrappy": "1" 149 | } 150 | }, 151 | "inherits": { 152 | "version": "2.0.3", 153 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 154 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 155 | "dev": true 156 | }, 157 | "minimatch": { 158 | "version": "3.0.4", 159 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 160 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 161 | "dev": true, 162 | "requires": { 163 | "brace-expansion": "^1.1.7" 164 | } 165 | }, 166 | "minimist": { 167 | "version": "0.0.8", 168 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 169 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 170 | "dev": true 171 | }, 172 | "mkdirp": { 173 | "version": "0.5.1", 174 | "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 175 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 176 | "dev": true, 177 | "requires": { 178 | "minimist": "0.0.8" 179 | } 180 | }, 181 | "mocha": { 182 | "version": "5.2.0", 183 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", 184 | "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", 185 | "dev": true, 186 | "requires": { 187 | "browser-stdout": "1.3.1", 188 | "commander": "2.15.1", 189 | "debug": "3.1.0", 190 | "diff": "3.5.0", 191 | "escape-string-regexp": "1.0.5", 192 | "glob": "7.1.2", 193 | "growl": "1.10.5", 194 | "he": "1.1.1", 195 | "minimatch": "3.0.4", 196 | "mkdirp": "0.5.1", 197 | "supports-color": "5.4.0" 198 | } 199 | }, 200 | "ms": { 201 | "version": "2.0.0", 202 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 203 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 204 | "dev": true 205 | }, 206 | "once": { 207 | "version": "1.4.0", 208 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 209 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 210 | "dev": true, 211 | "requires": { 212 | "wrappy": "1" 213 | } 214 | }, 215 | "path-is-absolute": { 216 | "version": "1.0.1", 217 | "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 218 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 219 | "dev": true 220 | }, 221 | "pathval": { 222 | "version": "1.1.0", 223 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 224 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", 225 | "dev": true 226 | }, 227 | "supports-color": { 228 | "version": "5.4.0", 229 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 230 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 231 | "dev": true, 232 | "requires": { 233 | "has-flag": "^3.0.0" 234 | } 235 | }, 236 | "type-detect": { 237 | "version": "4.0.8", 238 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 239 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 240 | "dev": true 241 | }, 242 | "wrappy": { 243 | "version": "1.0.2", 244 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 245 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 246 | "dev": true 247 | } 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "objectpath", 3 | "author": "Mike Marcacci ", 4 | "license": "MIT", 5 | "version": "2.0.0", 6 | "description": "Parse js object paths using both dot and bracket notation. Stringify an array of properties into a valid path.", 7 | "main": "index.js", 8 | "directories": { 9 | "test": "test" 10 | }, 11 | "scripts": { 12 | "test": "mocha test/*" 13 | }, 14 | "devDependencies": { 15 | "chai": "^4.2.0", 16 | "mocha": "^5.2.0" 17 | }, 18 | "keywords": [ 19 | "js object", 20 | "dot notation", 21 | "bracket notation", 22 | "path", 23 | "parse", 24 | "string", 25 | "object path", 26 | "object" 27 | ], 28 | "dependencies": {}, 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/mike-marcacci/objectpath.git" 32 | }, 33 | "bugs": { 34 | "url": "https://github.com/mike-marcacci/objectpath/issues" 35 | }, 36 | "homepage": "https://github.com/mike-marcacci/objectpath" 37 | } 38 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ObjectPath 2 | ========== 3 | 4 | [![Build Status](https://travis-ci.org/mike-marcacci/objectpath.svg?branch=master)](https://travis-ci.org/mike-marcacci/objectpath) 5 | 6 | Parse js object paths using both dot and bracket notation. Stringify an array of properties into a valid path. 7 | 8 | - parse JS object reference fragments 9 | - build JS object reference fragments 10 | - supports presence of unicode characters 11 | - supports presence of control characters in key names 12 | 13 | Parse a Path 14 | ------------ 15 | 16 | ObjectPath.parse(str) 17 | 18 | ```js 19 | var ObjectPath = require('objectpath'); 20 | 21 | ObjectPath.parse('a[1].b.c.d["e"]["f-f"].g'); 22 | // => ['a','1','b','c','d','e','f-f','g'] 23 | ``` 24 | 25 | Build a Path String 26 | ------------------- 27 | 28 | ObjectPath.stringify(arr, [quote="'"], [forceQuote=false]); 29 | 30 | ```js 31 | var ObjectPath = require('objectpath'); 32 | 33 | ObjectPath.stringify(['a','1','b','c','d-d','e']); 34 | // => "a[1].b.c['d-d'].e" 35 | 36 | 37 | ObjectPath.stringify(['a','1','b','c','d-d','e'],'"'); 38 | // => 'a[1].b.c["d-d"].e' 39 | 40 | 41 | ObjectPath.stringify(['a','1','b','c','d-d','e'],"'", true); 42 | // => "['a']['1']['b']['c']['d-d']['e']" 43 | ``` 44 | 45 | Normalize a Path 46 | ---------------- 47 | 48 | ObjectPath.normalize(str, [quote="'"], [forceQuote=false]) 49 | 50 | ```js 51 | var ObjectPath = require('objectpath'); 52 | 53 | ObjectPath.normalize('a[1].b.c.d["e"]["f-f"].g'); 54 | // => "a[1].b.c.d.e['f-f'].g" 55 | 56 | ObjectPath.normalize('a[1].b.c.d["e"]["f-f"].g', '"'); 57 | // => 'a[1].b.c.d.e["f-f"].g' 58 | 59 | ObjectPath.normalize('a[1].b.c.d["e"]["f-f"].g', "'", true); 60 | // => "['a']['1']['b']['c']['d']['e']['f-f']['g']" 61 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('chai').assert; 4 | var ObjectPath = require('../index.js'); 5 | 6 | var parse = ObjectPath.parse; 7 | var stringify = ObjectPath.stringify; 8 | 9 | describe('Parse', function(){ 10 | it('parses simple paths in dot notation', function(){ 11 | assert.deepEqual(parse('a'), ['a'], 'incorrectly parsed single node'); 12 | assert.deepEqual(parse('a.b.c'), ['a','b','c'], 'incorrectly parsed multi-node'); 13 | }); 14 | 15 | it('parses simple paths in bracket notation', function(){ 16 | assert.deepEqual(parse('["c"]'), ['c'], 'incorrectly parsed single headless node'); 17 | assert.deepEqual(parse('a["b"]["c"]'), ['a','b','c'], 'incorrectly parsed multi-node'); 18 | assert.deepEqual(parse('["a"]["b"]["c"]'), ['a','b','c'], 'incorrectly parsed headless multi-node'); 19 | }); 20 | 21 | it('parses a numberic nodes in bracket notation', function(){ 22 | assert.deepEqual(parse('[5]'), ['5'], 'incorrectly parsed single headless numeric node'); 23 | assert.deepEqual(parse('[5]["a"][3]'), ['5','a','3'], 'incorrectly parsed headless numeric multi-node'); 24 | }); 25 | 26 | it('parses a combination of dot and bracket notation', function(){ 27 | assert.deepEqual(parse('a[1].b.c.d["e"]["f"].g'), ['a','1','b','c','d','e','f','g']); 28 | }); 29 | 30 | it('parses unicode characters', function(){ 31 | assert.deepEqual(parse('∑´ƒ©∫∆.ø'), ['∑´ƒ©∫∆','ø'], 'incorrectly parsed unicode characters from dot notation'); 32 | assert.deepEqual(parse('["∑´ƒ©∫∆"]["ø"]'), ['∑´ƒ©∫∆','ø'], 'incorrectly parsed unicode characters from bracket notation'); 33 | }); 34 | 35 | it('parses nodes with control characters', function(){ 36 | assert.deepEqual(parse('["a.b."]'), ['a.b.'], 'incorrectly parsed dots from inside brackets'); 37 | assert.deepEqual(parse('["\""][\'\\\'\']'), ['"','\''], 'incorrectly parsed escaped quotes'); 38 | assert.deepEqual(parse('["\'"][\'"\']'), ['\'','"'], 'incorrectly parsed unescaped quotes'); 39 | assert.deepEqual(parse('["\\""][\'\\\'\']'), ['"','\''], 'incorrectly parsed escaped quotes'); 40 | assert.deepEqual(parse('[\'\\"\']["\\\'"]'), ['\\"','\\\''], 'incorrectly parsed escape characters'); 41 | assert.deepEqual(parse('["\\"]"]["\\"]\\"]"]'), ['"]','"]"]'], 'incorrectly parsed escape characters'); 42 | assert.deepEqual(parse('["[\'a\']"][\'[\\"a\\"]\']'), ['[\'a\']','[\\"a\\"]'], 'incorrectly parsed escape character'); 43 | }); 44 | }); 45 | 46 | describe('Stringify', function(){ 47 | it('stringifys simple paths with single quotes', function(){ 48 | assert.deepEqual(stringify(['a']), 'a', 'incorrectly stringified single node'); 49 | assert.deepEqual(stringify(['a','b','c']), 'a.b.c', 'incorrectly stringified multi-node'); 50 | assert.deepEqual(stringify(['a'], '\''), 'a', 'incorrectly stringified single node with excplicit single quote'); 51 | assert.deepEqual(stringify(['a','b','c'], '\''), 'a.b.c', 'incorrectly stringified multi-node with excplicit single quote'); 52 | }); 53 | 54 | it('stringifys simple paths with double quotes', function(){ 55 | assert.deepEqual(stringify(['a'],'"'), 'a', 'incorrectly stringified single node'); 56 | assert.deepEqual(stringify(['a','b','c'],'"'), 'a.b.c', 'incorrectly stringified multi-node'); 57 | }); 58 | 59 | it('stringifys a numberic nodes in bracket notation with single quotes', function(){ 60 | assert.deepEqual(stringify(['5']), '[5]', 'incorrectly stringified single headless numeric node'); 61 | assert.deepEqual(stringify(['5','a','3']), '[5].a[3]', 'incorrectly stringified headless numeric multi-node'); 62 | }); 63 | 64 | it('stringifys a numberic nodes in bracket notation with double quotes', function(){ 65 | assert.deepEqual(stringify(['5'],'"'), '[5]', 'incorrectly stringified single headless numeric node'); 66 | assert.deepEqual(stringify(['5','a','3'],'"'), '[5].a[3]', 'incorrectly stringified headless numeric multi-node'); 67 | }); 68 | 69 | it('stringifys a combination of dot and bracket notation with single quotes', function(){ 70 | assert.deepEqual(stringify(['a','1','b','c','d','e','f','g']), 'a[1].b.c.d.e.f.g'); 71 | assert.deepEqual(stringify(['a','1','b','c','d','e','f','g'],null,true), "['a']['1']['b']['c']['d']['e']['f']['g']"); 72 | }); 73 | 74 | it('stringifys a combination of dot and bracket notation with double quotes', function(){ 75 | assert.deepEqual(stringify(['a','1','b','c','d','e','f','g'],'"'), 'a[1].b.c.d.e.f.g'); 76 | assert.deepEqual(stringify(['a','1','b','c','d','e','f','g'],'"',true), '["a"]["1"]["b"]["c"]["d"]["e"]["f"]["g"]'); 77 | }); 78 | 79 | it('stringifys unicode characters with single quotes', function(){ 80 | assert.deepEqual(stringify(['∑´ƒ©∫∆']), '[\'∑´ƒ©∫∆\']', 'incorrectly stringified single node path with unicode'); 81 | assert.deepEqual(stringify(['∑´ƒ©∫∆','ø']), '[\'∑´ƒ©∫∆\'][\'ø\']', 'incorrectly stringified multi-node path with unicode characters'); 82 | }); 83 | 84 | it('stringifys unicode characters with double quotes', function(){ 85 | assert.deepEqual(stringify(['∑´ƒ©∫∆'],'"'), '["∑´ƒ©∫∆"]', 'incorrectly stringified single node path with unicode'); 86 | assert.deepEqual(stringify(['∑´ƒ©∫∆','ø'],'"'), '["∑´ƒ©∫∆"]["ø"]', 'incorrectly stringified multi-node path with unicode characters'); 87 | }); 88 | 89 | it("stringifys nodes with control characters and single quotes", function(){ 90 | assert.deepEqual(stringify(["a.b."],"'"), "['a.b.']", "incorrectly stringified dots from inside brackets"); 91 | assert.deepEqual(stringify(["'","\\\""],"'"), "['\\\'']['\\\\\"']", "incorrectly stringified escaped quotes"); 92 | assert.deepEqual(stringify(["\"","'"],"'"), "['\"']['\\'']", "incorrectly stringified unescaped quotes"); 93 | assert.deepEqual(stringify(["\\'","\\\""],"'"), "['\\\\\\'']['\\\\\"']", "incorrectly stringified escape character"); 94 | assert.deepEqual(stringify(["[\"a\"]","[\\'a\\']"],"'"), "['[\"a\"]']['[\\\\\\'a\\\\\\']']", "incorrectly stringified escape character"); 95 | }); 96 | 97 | it("stringifys nodes with backslash", function(){ 98 | const originalProperty = ' \\" \\\\" \\\\\\'; 99 | const path = stringify([' \\" \\\\" \\\\\\'], '"'); 100 | const finalProperty = JSON.parse(path.substring(1, path.length - 1)); 101 | 102 | assert.deepEqual(finalProperty, originalProperty, 'incorrectly stringified escaped backslash'); 103 | }); 104 | 105 | it('stringifys nodes with control characters and double quotes', function(){ 106 | assert.deepEqual(stringify(['a.b.'],'"'), '["a.b."]', 'incorrectly stringified dots from inside brackets'); 107 | assert.deepEqual(stringify(['"','\\\''],'"'), '["\\\""]["\\\\\'"]', 'incorrectly stringified escaped quotes'); 108 | assert.deepEqual(stringify(['\'','"'],'"'), '["\'"]["\\""]', 'incorrectly stringified unescaped quotes'); 109 | assert.deepEqual(stringify(['\\"','\\\''],'"'), '["\\\\\\""]["\\\\\'"]', 'incorrectly stringified escape character'); 110 | assert.deepEqual(stringify(['[\'a\']','[\\"a\\"]'],'"'), '["[\'a\']"]["[\\\\\\"a\\\\\\"]"]', 'incorrectly stringified escape character'); 111 | }); 112 | }); 113 | 114 | describe('Backslash support tests', function(){ 115 | var property, expected; 116 | function noBuckets(path) { 117 | return path.replace(/^\[(.*)\]$/, '$1'); 118 | } 119 | function digest(property) { 120 | return stringify(parse(property), '"'); 121 | } 122 | 123 | it('should escape a quote', function(){ 124 | assert.deepEqual(digest('a"'), String.raw`["a\""]`, 'incorrectly escape quote'); 125 | }); 126 | 127 | it('should escape backslash', function(){ 128 | assert.deepEqual(digest('lol\\\\eded.ededed.deede'), String.raw`["lol\\\\eded"].ededed.deede`, 'incorrectly escape quote'); 129 | }); 130 | 131 | it('should escape one backslash', function(){ 132 | property = String.raw`a\"`; 133 | expected = String.raw`["a\\\""]`; // equivalent: expected = '["a\\\\\\""]' 134 | assert.deepEqual(digest(property), expected, 'incorrectly escape single backslash'); 135 | assert.deepEqual(JSON.stringify(property), noBuckets(expected), 'incorrectly escape single backslash'); 136 | }); 137 | 138 | it('should escape several backslash', function(){ 139 | property = String.raw`a\\"`; 140 | expected = String.raw`["a\\\\\""]`; // equivalent: expected = '["a\\\\\\""]' 141 | assert.deepEqual(digest(property), expected, 'incorrectly escape several backslash'); 142 | assert.deepEqual(JSON.stringify(property), noBuckets(expected), 'incorrectly escape several backslash'); 143 | assert.deepEqual(digest(digest(digest(digest(property)))), expected, 'incorrectly escape after several stringify&parse on the same value'); 144 | }); 145 | 146 | it('should be a valid js path (accepted by ECMAScript)', function(){ 147 | property = String.raw`a\\"`; 148 | expected = String.raw`["a\\\\\""]`; // equivalent: expected = '["a\\\\\\""]' 149 | assert.deepEqual(digest(property), expected, 'invalid path syntax'); 150 | assert.doesNotThrow(function () { 151 | try { 152 | eval(digest(property)); // only with trusted string 153 | return true; 154 | } catch (e) { 155 | if (e.message.indexOf("Cannot read property") !== -1) // mean undefined 156 | return true; 157 | else // Syntax error 158 | throw new Error("wrong with ES"); 159 | } 160 | }, 'invalid path syntax'); 161 | }); 162 | }); 163 | 164 | describe('Normalize', function(){ 165 | it('normalizes a string', function(){ 166 | assert.deepEqual(ObjectPath.normalize('a.b["c"]'), 'a.b.c', 'incorrectly normalized a string with single quotes'); 167 | assert.deepEqual(ObjectPath.normalize('a.b["c"]','"'), 'a.b.c', 'incorrectly normalized a string with double quotes'); 168 | }); 169 | 170 | it('normalizes an array', function(){ 171 | assert.deepEqual(ObjectPath.normalize(['a','b','c']), 'a.b.c', 'incorrectly normalized an array with single quotes'); 172 | assert.deepEqual(ObjectPath.normalize(['a','b','c'],'"'), 'a.b.c', 'incorrectly normalized an array with double quotes'); 173 | }); 174 | }); 175 | --------------------------------------------------------------------------------