Skip to content

Commit

Permalink
fix(utility): add internal utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
bingtsingw committed Aug 18, 2024
1 parent f1ffa9e commit 05ff2f8
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/heavy-cameras-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@xstools/utility': patch
---

add internal utilities
3 changes: 3 additions & 0 deletions packages/utility/src/_internal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './pathToSegments';
export * from './stub';
export * from './toArgs';
42 changes: 42 additions & 0 deletions packages/utility/src/_internal/pathToSegments.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { describe, expect, test } from 'bun:test';
import { pathToSegments } from './pathToSegments';

describe('pathToSegments', () => {
test('normal usage', () => {
expect(pathToSegments('a.b.c')).toEqual(['a', 'b', 'c']);
expect(pathToSegments('a[b][c]')).toEqual(['a', 'b', 'c']);
expect(pathToSegments('a[b].c')).toEqual(['a', 'b', 'c']);
expect(pathToSegments('a["b.c"].d')).toEqual(['a', 'b.c', 'd']);
expect(pathToSegments('.a.b.c')).toEqual(['', 'a', 'b', 'c']);
});

test('empty', () => {
expect(pathToSegments('')).toEqual([]);
expect(pathToSegments('.')).toEqual(['', '']);
expect(pathToSegments('[]')).toEqual(['']);
expect(pathToSegments('a[].b')).toEqual(['a', '', 'b']);
});

test('one path', () => {
expect(pathToSegments('a')).toEqual(['a']);
expect(pathToSegments('[a]')).toEqual(['a']);
expect(pathToSegments('[a.b]')).toEqual(['a.b']);
expect(pathToSegments('["a.b"]')).toEqual(['a.b']);
expect(pathToSegments('"[a.b]"')).toEqual(['"', 'a.b', '"']);
});

test('complex', () => {
expect(pathToSegments('a[-1.23]["[\\"b\\"]"].c[\'[\\\'d\\\']\'][\ne\n][f].g')).toEqual([
'a',
'-1.23',
'["b"]',
'c',
"['d']",
'\ne\n',
'f',
'g',
]);

expect(pathToSegments('.a[b].c.d[e]["f.g"].h')).toEqual(['', 'a', 'b', 'c', 'd', 'e', 'f.g', 'h']);
});
});
65 changes: 65 additions & 0 deletions packages/utility/src/_internal/pathToSegments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* Converts a deep key string into an array of path segments.
*
* References: https://github.com/toss/es-toolkit/blob/main/src/compat/_internal/toPath.ts
*
* @example
* toSegments('') // => []
* toSegments('a.b.c') // => ['a', 'b', 'c']
* toSegments('a[b][c]') // => ['a', 'b', 'c']
* toSegments('.a.b.c') // => ['', 'a', 'b', 'c']
* toSegments('a["b.c"].d') // => ['a', 'b.c', 'd']
* toSegments('.a[b].c.d[e]["f.g"].h') // => ['', 'a', 'b', 'c', 'd', 'e', 'f.g', 'h']
*/
export function pathToSegments(deepKey: string): string[] {
const ESCAPE_REGEXP = /\\(\\)?/g;
const PROPERTY_REGEXP = RegExp(
// Match anything that isn't a dot or bracket.
'[^.[\\]]+' +
'|' +
// Or match property names within brackets.
'\\[(?:' +
// Match a non-string expression.
'([^"\'][^[]*)' +
'|' +
// Or match strings (supports escaping characters).
'(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' +
')\\]' +
'|' +
// Or match "" as the space between consecutive dots or empty brackets.
'(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))',
'g',
);

const result: string[] = [];

if (deepKey[0] === '.') {
result.push('');
}

let match: RegExpExecArray | null;
let lastIndex = 0;

while ((match = PROPERTY_REGEXP.exec(deepKey)) !== null) {
let key = match[0];
const expr = match[1];
const quote = match[2];
const substr = match[3];

if (quote && substr) {
key = substr.replace(ESCAPE_REGEXP, '$1');
} else if (expr) {
key = expr;
}

result.push(key);

if (PROPERTY_REGEXP.lastIndex === lastIndex) {
PROPERTY_REGEXP.lastIndex++;
} else {
lastIndex = PROPERTY_REGEXP.lastIndex;
}
}

return result;
}
15 changes: 15 additions & 0 deletions packages/utility/src/_internal/stub.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { toArgs } from './toArgs';

export const stubTrue = (): true => {
return true;
};

export const stubFalse = (): false => {
return false;
};

export const stubZero = (): number => {
return 0;
};

export const stubArgs = toArgs([1, 2, 3]);
14 changes: 14 additions & 0 deletions packages/utility/src/_internal/toArgs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { describe, expect, test } from 'bun:test';
import { toArgs } from './toArgs';

describe('toArgs', () => {
test('converts an array to an arguments object', () => {
const result = toArgs([1, 2, 3]);

expect(result.toString()).toBe('[object Arguments]');

(function (..._args) {
expect(arguments).toEqual(result);
})(1, 2, 3);
});
});
11 changes: 11 additions & 0 deletions packages/utility/src/_internal/toArgs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Converts an array to an `arguments` object.
*
* @example
* toArgs([1, 2, 3]); // { '0': 1, '1': 2, '2': 3 } as IArguments
*/
export const toArgs = (arr: unknown[]): IArguments => {
return (function (..._: unknown[]) {
return arguments;
})(...arr);
};

0 comments on commit 05ff2f8

Please sign in to comment.