From 7d659e78d43e69e6afc604e12b3ba3a5def0e46f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Proen=C3=A7a?= Date: Wed, 20 Dec 2023 12:26:00 +0000 Subject: [PATCH] [New] support enumerable Symbol properties --- index.js | 27 +++++++++++++++++++++++---- package.json | 1 + test/has.js | 22 ++++++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 07bfc52..256a700 100644 --- a/index.js +++ b/index.js @@ -30,6 +30,25 @@ var objectKeys = Object.keys || function keys(obj) { return res; }; +var propertyIsEnumerable = Object.prototype.propertyIsEnumerable; +var getOwnPropertySymbols = Object.getOwnPropertySymbols; // eslint-disable-line id-length + +// TODO: use reflect.ownkeys and filter out non-enumerables +function ownEnumerableKeys(obj) { + var res = objectKeys(obj); + + // Include enumerable symbol properties. + if (getOwnPropertySymbols) { + var symbols = getOwnPropertySymbols(obj); + for (var i = 0; i < symbols.length; i++) { + if (propertyIsEnumerable.call(obj, symbols[i])) { + res.push(symbols[i]); + } + } + } + return res; +} + // TODO: use object.hasown var hasOwnProperty = Object.prototype.hasOwnProperty || function (obj, key) { return key in obj; @@ -62,7 +81,7 @@ function copy(src) { dst = new T(); } - forEach(objectKeys(src), function (key) { + forEach(ownEnumerableKeys(src), function (key) { dst[key] = src[key]; }); return dst; @@ -124,7 +143,7 @@ function walk(root, cb, immutable) { function updateState() { if (typeof state.node === 'object' && state.node !== null) { if (!state.keys || state.node_ !== state.node) { - state.keys = objectKeys(state.node); + state.keys = ownEnumerableKeys(state.node); } state.isLeaf = state.keys.length === 0; @@ -281,7 +300,7 @@ Traverse.prototype.clone = function () { parents.push(src); nodes.push(dst); - forEach(objectKeys(src), function (key) { + forEach(ownEnumerableKeys(src), function (key) { dst[key] = clone(src[key]); }); @@ -300,7 +319,7 @@ function traverse(obj) { } // TODO: replace with object.assign? -forEach(objectKeys(Traverse.prototype), function (key) { +forEach(ownEnumerableKeys(Traverse.prototype), function (key) { traverse[key] = function (obj) { var args = [].slice.call(arguments, 1); var t = new Traverse(obj); diff --git a/package.json b/package.json index af4d4bc..7967084 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@ljharb/eslint-config": "^21.1.0", "aud": "^2.0.4", "auto-changelog": "^2.4.0", + "es-value-fixtures": "^1.4.2", "eslint": "=8.8.0", "in-publish": "^2.0.1", "npmignore": "^0.3.1", diff --git a/test/has.js b/test/has.js index e29fb51..6802a11 100644 --- a/test/has.js +++ b/test/has.js @@ -1,6 +1,7 @@ 'use strict'; var test = require('tape'); +var v = require('es-value-fixtures'); var traverse = require('../'); test('has', function (t) { @@ -13,5 +14,26 @@ test('has', function (t) { t.equal(traverse(obj).has(['a']), true); t.equal(traverse(obj).has(['a', 2]), false); + t.test('symbols', { skip: !v.hasSymbols }, function (st) { + /* eslint no-restricted-properties: 1 */ + var globalSymbol = Symbol.for('d'); + var localSymbol = Symbol('e'); + + obj[globalSymbol] = {}; + obj[globalSymbol][localSymbol] = 7; + obj[localSymbol] = 8; + + st.equal(traverse(obj).has([globalSymbol]), true); + st.equal(traverse(obj).has([globalSymbol, globalSymbol]), false); + st.equal(traverse(obj).has([globalSymbol, localSymbol]), true); + st.equal(traverse(obj).has([localSymbol]), true); + st.equal(traverse(obj).has([localSymbol]), true); + st.equal(traverse(obj).has([Symbol('e')]), false); + st.equal(traverse(obj).has([Symbol.for('d')]), true); + st.equal(traverse(obj).has([Symbol.for('e')]), false); + + st.end(); + }); + t.end(); });