Skip to content

Commit

Permalink
perf: reduce footprint
Browse files Browse the repository at this point in the history
  • Loading branch information
noomorph committed Oct 27, 2023
1 parent 073e47a commit dcff4c5
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 6 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"unicorn/prefer-string-replace-all": "off",
"unicorn/prevent-abbreviations": "off",
"unicorn/no-array-callback-reference": "off",
"unicorn/no-null": "off",
"unicorn/no-this-assignment": "off",
"unicorn/no-unused-properties": "off",
"unicorn/prefer-module": "off"
Expand Down
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@
"bunyan": "^2.0.5",
"bunyan-debug-stream": "^3.1.0",
"funpermaproxy": "^1.1.0",
"lodash.get": "^4.4.2",
"lodash.merge": "^4.6.2",
"lodash.set": "^4.3.2",
"node-ipc": "9.2.1",
"strip-ansi": "^6.0.0",
"tslib": "^2.5.3"
Expand Down
6 changes: 2 additions & 4 deletions src/metadata/containers/BaseMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/* eslint-disable unicorn/no-null */
import lodashGet from 'lodash.get';
import lodashMerge from 'lodash.merge';
import lodashSet from 'lodash.set';

import { logger, optimizeTracing } from '../../utils';
import { get as lodashGet, set as lodashSet, logger, optimizeTracing } from '../../utils';
import type { AggregatedIdentifier } from '../ids';
import * as symbols from '../symbols';
import type { Data, Metadata } from '../types';
Expand Down Expand Up @@ -58,7 +56,7 @@ export abstract class BaseMetadata implements Metadata {
throw new TypeError(`Cannot push a non-array value to path "${path}". Received: ${values}`);
}

const array = lodashGet(this[symbols.data], path, []);
const array = lodashGet(this[symbols.data], path, [] as unknown[]);
if (!Array.isArray(array)) {
throw new TypeError(
`Cannot push to path "${path}", because it is not an array, but: ${array}`,
Expand Down
52 changes: 52 additions & 0 deletions src/utils/get.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { get } from './get';

describe('get function', () => {
it('retrieves a simple property', () => {
expect(get({ a: 42 }, 'a')).toBe(42);
});

it('retrieves a nested property', () => {
expect(get({ a: { b: { c: 42 } } }, 'a.b.c')).toBe(42);
});

it('retrieves a nested property using array syntax', () => {
expect(get({ a: { b: { c: 42 } } }, ['a', 'b', 'c'])).toBe(42);
});

it('returns undefined for nonexistent properties', () => {
expect(get({}, 'a')).toBeUndefined();
});

it('retrieves a property that is explicitly set to null', () => {
expect(get({ a: null }, 'a')).toBeNull();
});

it('returns undefined for an incomplete path', () => {
expect(get({ a: 42 }, 'a.b')).toBeUndefined();
});

it('returns undefined for a nonexistent nested property', () => {
expect(get({ a: { b: 42 } }, 'a.b.c')).toBeUndefined();
});

it('returns undefined when object is a primitive', () => {
expect(get(42, 'a')).toBeUndefined();
});

it('returns undefined when object is null', () => {
expect(get(null, 'a')).toBeUndefined();
});

it('returns undefined when object is undefined', () => {
expect(get(undefined, 'a')).toBeUndefined();
});

it('returns the default value if provided and property is not found', () => {
expect(get({}, 'a', 'default')).toBe('default');
});

it('infers the return type from the default value', () => {
const result: number | undefined = get({}, 'a', 42);
expect(result).toBe(42);
});
});
23 changes: 23 additions & 0 deletions src/utils/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export function get<T>(obj: unknown, path: string | readonly string[]): T | undefined;
export function get<T>(obj: unknown, path: string | readonly string[], defaultValue: T): T;
export function get<T = unknown>(
obj: unknown,
path: string | readonly string[],
defaultValue?: T,
): T | undefined {
if (obj == null) {
return defaultValue;
}

const pathArray = typeof path === 'string' ? path.split('.') : path;
let it: any = obj;

for (const key of pathArray) {
if (it[key] === undefined) {
return defaultValue;
}
it = it[key];
}

return it;
}
2 changes: 2 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export * from './emitters';
export * as jestUtils from './jestUtils';
export { getVersion } from './getVersion';
export { aggregateLogs, logger, optimizeTracing } from './logger';
export { get } from './get';
export { set } from './set';
export { makeDeferred, Deferred } from './makeDeferred';
export { memoizeArg1 } from './memoizeArg1';
export { memoizeLast } from './memoizeLast';
Expand Down
39 changes: 39 additions & 0 deletions src/utils/isPrimitive.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { isPrimitive } from './isPrimitive';

describe('isPrimitive function', () => {
it('returns true for numbers', () => {
expect(isPrimitive(42)).toBe(true);
});

it('returns true for strings', () => {
expect(isPrimitive('Hello')).toBe(true);
});

it('returns true for booleans', () => {
expect(isPrimitive(true)).toBe(true);
expect(isPrimitive(false)).toBe(true);
});

it('returns true for null', () => {
expect(isPrimitive(null)).toBe(true);
});

it('returns true for undefined', () => {
expect(isPrimitive(void 0)).toBe(true);
});

it('returns false for objects', () => {
expect(isPrimitive({})).toBe(false);
expect(isPrimitive({ key: 'value' })).toBe(false);
});

it('returns false for arrays', () => {
expect(isPrimitive([])).toBe(false);
expect(isPrimitive([1, 2, 3])).toBe(false);
});

it('returns false for functions', () => {
expect(isPrimitive(() => {})).toBe(false);
expect(isPrimitive(function () {})).toBe(false);
});
});
8 changes: 8 additions & 0 deletions src/utils/isPrimitive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function isPrimitive(value: unknown): boolean {
return (
typeof value === 'number' ||
typeof value === 'string' ||
typeof value === 'boolean' ||
value == null
);
}
40 changes: 40 additions & 0 deletions src/utils/set.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { set } from './set';

describe('set function', () => {
it('should set a value for a simple path', () => {
const obj: any = {};
set(obj, 'a', 42);
expect(obj.a).toBe(42);
});

it('should set a value for a nested path', () => {
const obj: any = {};
set(obj, 'a.b.c', 42);
expect(obj.a.b.c).toBe(42);
});

it('should set a value for a nested path with array syntax', () => {
const obj: any = {};
set(obj, ['a', 'b', 'c'], 42);
expect(obj.a.b.c).toBe(42);
});

it('should not set a value for unsafe keys', () => {
const obj: any = {};
set(obj, '__proto__.unsafe', 42);
expect(obj.unsafe).toBeUndefined();
});

it('should override existing properties', () => {
const obj = { a: 1 };
set(obj, 'a', 42);
expect(obj.a).toBe(42);
});

it.each([[null], [undefined], [42], ['string'], [true]])(
'should handle non-object targets gracefully: %j',
(obj: unknown) => {
expect(() => set(obj, 'a', 42)).not.toThrow();
},
);
});
31 changes: 31 additions & 0 deletions src/utils/set.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { isPrimitive } from './isPrimitive';

export function set(obj: unknown, path: string | readonly string[], value: unknown): void {
if (isPrimitive(obj)) {
return;
}

const pathArray = typeof path === 'string' ? path.split('.') : path;
let it: any = obj;

for (let i = 0; i < pathArray.length; i++) {
const key = pathArray[i];
if (isUnsafeKey(key)) {
return;
}

if (i === pathArray.length - 1) {
// If it's the last key in the path
it[key] = value;
} else {
if (it[key] === undefined) {
it[key] = {};
}
it = it[key];
}
}
}

function isUnsafeKey(key: string): boolean {
return key === '__proto__' || key === 'constructor' || key === 'prototype';
}

0 comments on commit dcff4c5

Please sign in to comment.