Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/path/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {ABSOLUTE_START} from './constants.js';
import {isAbsolute, isRelative} from './predicates.js';

/**
* Make a path explicitly absolute when it is not already.
*
* @param {string} path - Path to make explicitly absolute
* @returns {string} Absolute path (i.e. starts with `$.`)
*/
export const absolute = (path) => isAbsolute(path) ? path : ABSOLUTE_START + path;

/**
* Make a path relative when it is not already.
*
* @param {string} path - Path to make relative
* @returns {string} Relative path (i.e. does not start with `$.`)
*/
export const relative = (path) => isRelative(path) ? path : path.slice(2);

/**
* Extract root field from path, excluding absolute path prefix `$.`.
*
* @param {string} path - Field path from which to extract root field
* @returns {string} Root field identifier
*/
export const root = (path) => path.match(/^(?:\$\.)?([a-zA-Z0-9_]+)/)?.[1];
42 changes: 42 additions & 0 deletions src/path/helpers.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {absolute, relative, root} from './helpers.js';

describe('absolute', () => {
test.each([
['field1', '$.field1'],
['$.field1', '$.field1'],
['$.level1.level2', '$.level1.level2'],
['level1.level2', '$.level1.level2'],
['collection[0]', '$.collection[0]'],
['$.collection[0]', '$.collection[0]'],
])(`should path '%s' be root: %s`, (path, expectedAbsolute) => {
expect(absolute(path)).toBe(expectedAbsolute);
});
});

describe('relative', () => {
test.each([
['field1', 'field1'],
['$.field1', 'field1'],
['$.level1.level2', 'level1.level2'],
['level1.level2', 'level1.level2'],
['collection[0]', 'collection[0]'],
['$.collection[0]', 'collection[0]'],
])(`should path '%s' be root: %s`, (path, expectedRelative) => {
expect(relative(path)).toBe(expectedRelative);
});
});

describe('root', () => {
test.each([
['$', undefined],
['$.', undefined],
['field1', 'field1'],
['$.field1', 'field1'],
['$.level1.level2', 'level1'],
['level1.level2', 'level1'],
['collection[0]', 'collection'],
['$.collection[0]', 'collection'],
])(`should path '%s' be root: %s`, (path, expectedRoot) => {
expect(root(path)).toBe(expectedRoot);
});
});
1 change: 1 addition & 0 deletions src/path/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './constants.js';
export * from './helpers.js';
export * from './predicates.js';
23 changes: 23 additions & 0 deletions src/path/predicates.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
import isMetadata from '../metadata/is-metadata.js';
import {ABSOLUTE_START} from './constants.js';

/**
* Test whether a path is explicitly absolute.
*
* @param {string} path - Path to be tested
* @returns {boolean} Whether the path is explicitly absolute (i.e. starts with `$.`)
*/
export const isAbsolute = (path) => path.slice(0,2) === ABSOLUTE_START;

/**
* Test whether a path is relative.
*
* @param {string} path - Path to be tested
* @returns {boolean} Whether the path is relative (i.e. does not start with `$.`)
*/
export const isRelative = (path) => !isAbsolute(path);

/**
* Test whether a path points to a root field.
* Note: This will return true for relative paths targeting top-level members for relative to current location.
*
* @param {string} path - Path to be tested
* @returns {boolean} Whether the path points to a root field
*/
export const isRoot = (path) => !!path.match(/^(?:\$\.)?[a-zA-Z0-9_]+$/);

export {
// Reexport `isMetadata()` for convenience
isMetadata,
Expand Down
42 changes: 42 additions & 0 deletions src/path/predicates.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {isAbsolute, isRelative, isRoot} from './predicates.js';

describe('isAbsolute', () => {
test.each([
['field1', false],
['$.field1', true],
['$.level1.level2', true],
['level1.level2', false],
['collection[0]', false],
['$.collection[0]', true],
['collection[0]', false],
])(`should path '%s' be root: %s`, (path, expectedAbsolute) => {
expect(isAbsolute(path)).toBe(expectedAbsolute);
});
});

describe('isRelative', () => {
test.each([
['field1', true],
['$.field1', false],
['$.level1.level2', false],
['level1.level2', true],
['collection[0]', true],
['$.collection[0]', false],
['collection[0]', true],
])(`should path '%s' be root: %s`, (path, expectedRelative) => {
expect(isRelative(path)).toBe(expectedRelative);
});
});

describe('isRoot', () => {
test.each([
['field1', true],
['$.field1', true],
['$.level1.level2', false],
['level1.level2', false],
['$.collection[0]', false],
['collection[0]', false],
])(`should path '%s' be root: %s`, (path, expectedRoot) => {
expect(isRoot(path)).toBe(expectedRoot);
});
});