Skip to content

Commit

Permalink
Fix null and undefineds bug; Mangle top-level identifiers; (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
sdgluck authored Jul 25, 2017
1 parent aa391da commit bae5267
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 28 deletions.
9 changes: 9 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"presets": [
["babili", {
"mangle": {
"topLevel": true
}
}]
]
}
45 changes: 21 additions & 24 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,35 @@
let a = Array.isArray
let o = v => typeof v === 'object'
let k = v => a(v)
? Object.keys([...v]) // use spread to preserve holes in array
: Object.keys(v)
let isArray = Array.isArray
let isObject = v => typeof v === 'object' && v !== null
let keys = v => Object.keys(isArray(v) ? [...v] : v) // use spread to preserve holes in array

/** @returns {Array|Object} */
let M = module.exports = (parent, targetPath = []) => {
if (typeof parent !== 'object') {
throw new Error('expect arr|obj')
}

// we re-use this in order to reduce the number of let declarations
let multiPurpose

let getOrSetTarget = (obj, value) => {
multiPurpose = targetPath.length
for (;multiPurpose > (value ? 1 : 0);) {
while (multiPurpose > (value ? 1 : 0)) {
obj = obj[targetPath[--multiPurpose]]
}
if (!value) return obj
obj[targetPath[--multiPurpose]] = value
}

let target = getOrSetTarget(parent)
let isTargetArray = a(target)
let multiPurpose // we re-use this in order to reduce the number of let declarations
let parentClone

let clone = (obj = parent) =>
o(obj) ? k(obj).reduce((newObj, key) => (
isObject(obj) ? keys(obj).reduce((newObj, key) => (
multiPurpose = obj[key],
newObj[key] = a(multiPurpose)
newObj[key] = isArray(multiPurpose)
? multiPurpose.map(clone)
: o(multiPurpose)
: isObject(multiPurpose)
? clone(multiPurpose)
: multiPurpose,
/* return */newObj
), a(obj) ? [] : {}) : obj
), isArray(obj) ? [] : {}) : obj

let mutationTrapError = () => {
throw new Error((isTargetArray ? 'arr' : 'obj') + ' immutable')
throw new Error(`${isArray(target) ? 'arr' : 'obj'} immutable`)
}

let override = prop => (...args) => {
Expand All @@ -54,18 +46,23 @@ let M = module.exports = (parent, targetPath = []) => {
? [multiPurpose, M(cl)] : multiPurpose
}

if (!isObject(parent)) {
throw new Error('expect arr|obj')
} else if (!target) {
return target
}

if (!targetPath.length) {
parent = k(parent).reduce((newObj, key) => (
newObj[key] = o(target[key])
parent = keys(parent).reduce((newObj, key) => (
newObj[key] = isObject(target[key])
? M(parent, [...targetPath, key])
: target[key],
/* return */newObj
), a(parent) ? [] : {})
), isArray(parent) ? [] : {})
}

return new Proxy(target, {
get (_, prop) {
let parentClone
multiPurpose = getOrSetTarget(parent)
return {
$set (prop, val) {
Expand All @@ -76,7 +73,7 @@ let M = module.exports = (parent, targetPath = []) => {
},
$unset (prop) {
parentClone = clone()
if (isTargetArray && !(prop % 1) && prop >= 0) {
if (isArray(target) && !(prop % 1) && prop >= 0) {
multiPurpose = [
...parentClone.slice(0, prop),
...parentClone.slice(prop + 1)
Expand Down
2 changes: 1 addition & 1 deletion lib/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "mewt",
"version": "2.0.0",
"version": "2.0.1",
"description": "Immutability in under one kilobyte",
"main": "lib/index.js",
"scripts": {
"build": "rimraf lib && babili index.js -d lib",
"build": "rimraf lib && babel index.js -d lib",
"lint": "eslint index.js",
"test": "npm run build && jest",
"prepublishOnly": "npm run lint && npm run build",
Expand All @@ -15,7 +15,9 @@
"author": "Sam Gluck <sdgluck@gmail.com>",
"license": "MIT",
"devDependencies": {
"babili": "0.0.12",
"babel-cli": "6.24.1",
"babel-core": "6.25.0",
"babel-preset-babili": "0.1.4",
"eslint": "3.18.0",
"eslint-config-standard": "7.1.0",
"eslint-plugin-promise": "3.5.0",
Expand Down
10 changes: 10 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ describe('mewt', () => {
expect(n).toEqual(a)
})

it('should handle null, undefined, and array holes', () => {
const a = mewt([null, undefined, , ])
expect(() => a.forEach(v => v)).not.toThrow()
})

it('should throw on mutation', () => {
const a = mewt([])
expect(() => a[0] = 'Lodger').toThrowError(/immutable/)
Expand Down Expand Up @@ -225,6 +230,11 @@ describe('mewt', () => {
expect(o).not.toBe(n)
})

it('should handle null and undefined property values', () => {
const a = mewt({ a: null, b: undefined })
expect(() => Object.keys(a).forEach(k => a[k])).not.toThrow()
})

it('should throw on mutation', () => {
const o = mewt({})
expect(() => o.track = 'The Promise').toThrowError(/immutable/)
Expand Down

0 comments on commit bae5267

Please sign in to comment.