Comparing version 0.0.2 to 0.0.3
@@ -0,4 +1,7 @@ | ||
export { revise as reviseOperator } from './operators/revise'; | ||
export { retainFn } from './retainFn'; | ||
export { revise } from './revise'; | ||
export { reviseFn } from './reviseFn'; | ||
export { retainFn } from './retainFn'; | ||
export { reviseReducer } from './reviseReducer'; | ||
export { smartEach } from './smartEach'; | ||
export { smartMap } from './smartMap'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var revise_1 = require("./revise"); | ||
exports.revise = revise_1.revise; | ||
var revise_1 = require("./operators/revise"); | ||
exports.reviseOperator = revise_1.revise; | ||
var retainFn_1 = require("./retainFn"); | ||
exports.retainFn = retainFn_1.retainFn; | ||
var revise_2 = require("./revise"); | ||
exports.revise = revise_2.revise; | ||
var reviseFn_1 = require("./reviseFn"); | ||
exports.reviseFn = reviseFn_1.reviseFn; | ||
var retainFn_1 = require("./retainFn"); | ||
exports.retainFn = retainFn_1.retainFn; | ||
var reviseReducer_1 = require("./reviseReducer"); | ||
exports.reviseReducer = reviseReducer_1.reviseReducer; | ||
var smartEach_1 = require("./smartEach"); | ||
exports.smartEach = smartEach_1.smartEach; | ||
var smartMap_1 = require("./smartMap"); | ||
exports.smartMap = smartMap_1.smartMap; | ||
//# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
export declare const isObject: (obj: {}) => obj is object; | ||
export declare const isObject: (obj: any) => obj is object; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
// tslint:disable-next-line no-any We allow any object to be checked. | ||
exports.isObject = (obj) => { | ||
return typeof obj === 'object'; | ||
return typeof obj === 'object' && obj != null; | ||
}; | ||
//# sourceMappingURL=isObject.js.map |
@@ -7,3 +7,5 @@ "use strict"; | ||
let output; | ||
// TODO(asif): Use WeakMap to allow obj dealloc. | ||
return function (...newArgs) { | ||
// TODO(#27): reviseFn instead of shallowArrayEqual. | ||
const unchanged = shallowArrayEqual_1.shallowArrayEqual(lastArgs, newArgs); | ||
@@ -17,1 +19,2 @@ if (!unchanged) { | ||
}; | ||
//# sourceMappingURL=retainFn.js.map |
@@ -1,1 +0,5 @@ | ||
export declare const revise: <T>(source: T, revision: T) => T; | ||
import { WalkFilter } from './walkTree'; | ||
export interface ReviseOptions { | ||
prefilter?: WalkFilter; | ||
} | ||
export declare const revise: <T>(source: T, revision: T, reviseOptions?: ReviseOptions) => T; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const walkTree_1 = require("./walkTree"); | ||
exports.revise = (source, revision) => { | ||
return walkTree_1.walkTree(source, revision); | ||
exports.revise = (source, revision, reviseOptions = {}) => { | ||
const { prefilter } = reviseOptions; | ||
// TODO(asif): See if we can get this type working. | ||
const nodeSet = new Set(); | ||
return walkTree_1.walkTree(source, revision, { | ||
nodeSet, | ||
path: [], | ||
prefilter, | ||
}); | ||
}; | ||
//# sourceMappingURL=revise.js.map |
export declare type callback<K, A, M> = (this: K, ...args: A[]) => M; | ||
export declare const reviseFn: <K, A, M>(fn: callback<K, A, M>) => (...args: A[]) => M; | ||
export declare const reviseFn: <K, A, M>(fn: callback<K, A, M>, initial?: M | undefined) => (...args: A[]) => M; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const revise_1 = require("./revise"); | ||
exports.reviseFn = (fn) => { | ||
let source; | ||
exports.reviseFn = (fn, initial) => { | ||
let source = initial; | ||
return function (...args) { | ||
@@ -12,1 +12,2 @@ const revision = fn.apply(this, args); | ||
}; | ||
//# sourceMappingURL=reviseFn.js.map |
import { AnyAction, Reducer } from 'redux'; | ||
/** | ||
* EvilDiff.revise wrapper for redux. | ||
* | ||
* Usage: | ||
* createStore(reviseReducer(rootReducer)); | ||
*/ | ||
export declare const reviseReducer: <S>(reducer: Reducer<S>) => (state: S, action: AnyAction) => S; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const revise_1 = require("./revise"); | ||
/** | ||
* EvilDiff.revise wrapper for redux. | ||
* | ||
* Usage: | ||
* createStore(reviseReducer(rootReducer)); | ||
*/ | ||
exports.reviseReducer = (reducer) => { | ||
@@ -9,1 +15,2 @@ return (state, action) => { | ||
}; | ||
//# sourceMappingURL=reviseReducer.js.map |
@@ -17,1 +17,2 @@ "use strict"; | ||
}; | ||
//# sourceMappingURL=shallowArrayEqual.js.map |
@@ -5,5 +5,9 @@ "use strict"; | ||
const base = Array.isArray(source) ? [] : {}; | ||
// TODO(asif): Remove this hack when TS/pull/13288 is merged in | ||
const sourceObj = source; | ||
// Explicitly use Object.assign to avoid array to object conversion. | ||
// tslint:disable-next-line prefer-object-spread | ||
const cloneObj = Object.assign(base, sourceObj); | ||
return cloneObj; | ||
}; | ||
//# sourceMappingURL=shallowClone.js.map |
@@ -1,1 +0,11 @@ | ||
export declare const walkTree: <T>(source: T, revision: T) => T; | ||
export declare type WalkFilter = (path: string[], value: any, newValue: any) => boolean; | ||
export interface TreeWalkerOptions { | ||
nodeSet: Set<{}>; | ||
path: string[]; | ||
prefilter?: WalkFilter; | ||
} | ||
/** | ||
* Recursively walks tree, copy values to revision object and cloning | ||
* paths that have cloned. | ||
*/ | ||
export declare function walkTree<T>(source: T, revision: T, options: TreeWalkerOptions): T; |
@@ -5,9 +5,62 @@ "use strict"; | ||
const shallowClone_1 = require("./shallowClone"); | ||
exports.walkTree = (source, revision) => { | ||
/* | ||
* We want to return a Symbol from crawl that lets us know we hit a circular | ||
* dep. In order to work with typescript, we have to trick it in a few ways. | ||
* | ||
* First, we need to tell it that CIRCULAR_DEP_DETECTED is of type CrawlEscape. | ||
* This is handled by casting to any to string. | ||
* | ||
* Second, we the caller function, walkTree, to identify the Symbol, without | ||
* having newValue muddled ed as T|CrawlEscape. But because we cast a symbol to | ||
* string, we can't do newValue === 'CIRCULAR_DEP_DETECTED', which would allow | ||
* typescript to infer that newValue wouldn't be CIRCULAR_DEP_DETECTED after | ||
* that line. To get around that, we implement isCrawlEscape that does the | ||
* symbol check for us. | ||
*/ | ||
const CIRCULAR_DEP_DETECTED = Symbol(); | ||
const crawl = (source, revision, options) => { | ||
const { nodeSet, path, prefilter, } = options; | ||
// Crawl assumes path is up to date. | ||
if (prefilter && prefilter(path, source, revision)) { | ||
return source; | ||
} | ||
if (isObject_1.isObject(source)) { | ||
if (nodeSet.has(source)) { | ||
// Source will be removed by callee from the set. | ||
return CIRCULAR_DEP_DETECTED; | ||
} | ||
else { | ||
nodeSet.add(source); | ||
} | ||
} | ||
const newValue = walkTree(source, revision, options); | ||
if (isObject_1.isObject(source)) { | ||
nodeSet.delete(source); | ||
} | ||
return newValue; | ||
}; | ||
/** | ||
* Hack to transform CIRCULAR_DEP_DETECTED to CrawlEscape. | ||
*/ | ||
// tslint:disable-next-line no-any We allow any object to be checked. | ||
const isCrawlEscape = (value) => { | ||
return value === CIRCULAR_DEP_DETECTED; | ||
}; | ||
/** | ||
* Recursively walks tree, copy values to revision object and cloning | ||
* paths that have cloned. | ||
*/ | ||
// tslint:disable-next-line only-arrow-functions crawl needs hoisting. | ||
function walkTree(source, revision, options) { | ||
const { path, prefilter, } = options; | ||
// Return early for no op. | ||
if (source === revision) { | ||
return source; | ||
} | ||
// Return revision if its a non object type. | ||
// This also catches null/undefined values. | ||
if (!isObject_1.isObject(source) || !isObject_1.isObject(revision)) { | ||
return revision; | ||
} | ||
// Check if both objects are the same. This catches Arr/Obj merging. | ||
const matchingType = source instanceof revision.constructor && | ||
@@ -18,18 +71,77 @@ revision instanceof source.constructor; | ||
} | ||
// Gather keys. For arrays, its just a list of indexes. | ||
// TODO(asif): Test walking arrays instead of getting its keys. | ||
const sourceKeys = Object.keys(source); | ||
let changed = false; | ||
const revisionKeys = Object.keys(revision); | ||
let cloned = false; | ||
let deletedCount = 0; | ||
for (const key of sourceKeys) { | ||
const sourceValue = source[key]; | ||
const revisionValue = revision[key]; | ||
const newValue = exports.walkTree(sourceValue, revisionValue); | ||
const revisionValue = key in revision ? revision[key] : undefined; | ||
// TOOD(asif): Optimize path by only mutating if prefilter is set. | ||
// Add key to the path before crawling. | ||
path.push(key); | ||
// Recursive call. If newValue is deeply equal to sourceValue, then | ||
// we return sourceValue, allowing us to skip cloning. | ||
// To handle deleted property via undefined, we need to explictly set this | ||
// return type. | ||
const newValue = crawl(sourceValue, revisionValue, options); | ||
// Remove key from path after crawling. | ||
path.pop(); | ||
// Check to see if we hit a circular dependency. | ||
if (isCrawlEscape(newValue)) { | ||
return revisionValue; | ||
} | ||
// If walkTree doesn't return new object, that means no change. | ||
if (newValue === sourceValue) { | ||
continue; | ||
} | ||
if (!changed && isObject_1.isObject(source)) { | ||
// On first change, clone current object. | ||
// This needs to be done before we set the newValue to the property, | ||
// so we don't modify the original object. | ||
// TODO(asif): Remove isObject call when TS/pull/13288 is merged in. | ||
if (!cloned && isObject_1.isObject(source)) { | ||
source = shallowClone_1.shallowClone(source); | ||
changed = true; | ||
cloned = true; | ||
} | ||
source[key] = newValue; | ||
if (newValue === undefined) { | ||
deletedCount++; | ||
// TODO(asif): Investigate performance of delete vs setting | ||
// undefined. | ||
// TODO(asif): Add configuration property to prefer undefined vs | ||
// delete. | ||
delete source[key]; | ||
} | ||
else { | ||
source[key] = newValue; | ||
} | ||
} | ||
// Check if there are any new keys in revision object. | ||
if (sourceKeys.length - deletedCount === revisionKeys.length) { | ||
return source; | ||
} | ||
// TODO(asif): Optimize this best O(n) to worst O(n) by switching to a | ||
// set. | ||
for (const key of revisionKeys) { | ||
// TODO(asif): Use deletedCount to return early. | ||
const sourceHasKey = key in source; | ||
// TOOD(asif): Optimize path by only mutating if prefilter is set. | ||
if (!sourceHasKey) { | ||
path.push(key); | ||
if (prefilter && prefilter(path, source[key], revision[key])) { | ||
continue; | ||
} | ||
path.pop(); | ||
// There are new keys, so we should clone if we haven't done so already. | ||
// TODO(asif): Remove isObject call when TS/pull/13288 is merged in. | ||
if (!cloned && isObject_1.isObject(source)) { | ||
source = shallowClone_1.shallowClone(source); | ||
cloned = true; | ||
} | ||
source[key] = revision[key]; | ||
} | ||
} | ||
return source; | ||
}; | ||
} | ||
exports.walkTree = walkTree; | ||
//# sourceMappingURL=walkTree.js.map |
{ | ||
"name": "evil-diff", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "evil-diff", | ||
@@ -18,3 +18,3 @@ "license": "MIT", | ||
"clean": "gts clean", | ||
"compile": "tsc -p .", | ||
"compile": "yarn run clean; tsc -p . --project tsconfig.build.json", | ||
"coverage": "nyc --include='src/**/*.ts' --reporter=text --reporter=html --reporter=lcov mocha --compilers ts:ts-node/register --recursive 'test/**/*-spec.ts'", | ||
@@ -26,3 +26,3 @@ "fix": "gts fix", | ||
"pretest": "yarn run lint && yarn run check && yarn run compile", | ||
"test": "mocha --compilers ts:ts-node/register --recursive 'test/**/*-spec.ts' 'src/**/*.ts'", | ||
"test": "yarn run clean; mocha --compilers ts:ts-node/register --recursive 'test/**/*-spec.ts' 'src/**/*.ts'", | ||
"version": "tsc --version", | ||
@@ -47,2 +47,3 @@ "watch": "yarn run compile --watch", | ||
"rimraf": "^2.0.0", | ||
"rxjs": "^5.5.2", | ||
"sinon": "^2.2.0", | ||
@@ -49,0 +50,0 @@ "ts-node": "^3.3.0", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
29581
19
39
365
1