Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Mar 6, 2022
1 parent ba3a6bd commit 6a36220
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 77 deletions.
36 changes: 12 additions & 24 deletions src/config/normalize/lib/star_dot_path/entries.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import { serialize } from './parsing/serialize.js'
import { isAnyToken, getAnyEntries } from './tokens/any.js'
import {
isIndexToken,
getIndexEntries,
handleIndexMissingValue,
} from './tokens/array.js'
import { getPropEntries, handlePropMissingValue } from './tokens/prop.js'
import { isRegExpToken, getRegExpEntries } from './tokens/regexp.js'
import { getTokenType } from './tokens/main.js'

// List all values (and their associated path) matching a specific query for
// on specific target value.
Expand All @@ -27,19 +20,14 @@ const listTokenEntries = function (entries, token) {
}

const getTokenEntries = function ({ value, path }, token) {
if (isAnyToken(token)) {
return getAnyEntries(value, path)
}

if (isRegExpToken(token)) {
return getRegExpEntries(value, path, token)
}

if (isIndexToken(token)) {
return getIndexEntries(value, path, token)
}

return getPropEntries(value, path, token)
const tokenType = getTokenType(token)
const { value: valueA, missing } = tokenType.handleMissingValue(value)
const entries = tokenType.getEntries(valueA, path, token)
return entries.map((entry) => ({
value: entry.value,
path: entry.path,
missing,
}))
}

// When the value does not exist, we set it deeply with `set()` but not with
Expand All @@ -50,9 +38,9 @@ const getTokenEntries = function ({ value, path }, token) {
// - Positive are kept
// - Negative are converted to index 0
export const handleMissingValue = function (value, token) {
return isIndexToken(token)
? handleIndexMissingValue(value)
: handlePropMissingValue(value)
const tokenType = getTokenType(token)
const { value: valueA } = tokenType.handleMissingValue(value)
return valueA
}

// Compute all entries properties from the basic ones
Expand Down
20 changes: 3 additions & 17 deletions src/config/normalize/lib/star_dot_path/parsing/serialize.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { isAnyToken, serializeAnyToken } from '../tokens/any.js'
import { isIndexToken, serializeIndexToken } from '../tokens/array.js'
import { serializePropToken } from '../tokens/prop.js'
import { isRegExpToken, serializeRegExpToken } from '../tokens/regexp.js'
import { getTokenType } from '../tokens/main.js'
import { SEPARATOR } from '../tokens/special.js'

import { parse } from './parse.js'
Expand All @@ -19,17 +16,6 @@ const serializePath = function (path) {
}

const serializeToken = function (token, index) {
if (isAnyToken(token)) {
return serializeAnyToken()
}

if (isIndexToken(token)) {
return serializeIndexToken(token)
}

if (isRegExpToken(token)) {
return serializeRegExpToken(token)
}

return serializePropToken(token, index)
const tokenType = getTokenType(token)
return tokenType.serialize(token, index)
}
12 changes: 2 additions & 10 deletions src/config/normalize/lib/star_dot_path/parsing/validate.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { inspect } from 'util'

import { isAnyToken } from '../tokens/any.js'
import { isIndexToken } from '../tokens/array.js'
import { isPropToken } from '../tokens/prop.js'
import { isRegExpToken } from '../tokens/regexp.js'
import { getTokenType } from '../tokens/main.js'

// Most methods accept both query and path syntaxes.
// This checks which one is used.
Expand Down Expand Up @@ -39,12 +36,7 @@ const validateToken = function (token, path) {
}

const isValidToken = function (token) {
return (
isPropToken(token) ||
isIndexToken(token) ||
isAnyToken(token) ||
isRegExpToken(token)
)
return getTokenType(token) !== undefined
}

const throwPathError = function (path, message) {
Expand Down
2 changes: 1 addition & 1 deletion src/config/normalize/lib/star_dot_path/set.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const setEntry = function ({ target, path, value, index }) {
}

const key = path[index]
const { value: defaultedTarget } = handleMissingValue(target, key)
const defaultedTarget = handleMissingValue(target, key)
const childTarget = defaultedTarget[key]
const childValue = setEntry({
target: childTarget,
Expand Down
18 changes: 15 additions & 3 deletions src/config/normalize/lib/star_dot_path/tokens/any.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { isRecurseObject } from './recurse.js'
import { ANY, ESCAPE, SEPARATOR } from './special.js'

// Check if a token is *
export const isAnyToken = function (token) {
const test = function (token) {
return isPlainObj(token) && token.type === ANY_TYPE
}

const ANY_TYPE = 'any'

export const serializeAnyToken = function () {
const serialize = function () {
return ANY
}

Expand All @@ -27,9 +27,14 @@ Otherwise, please escape it with a "${ESCAPE}".`,
return { type: ANY_TYPE }
}

// When missing, there are no entries, so no need to add missing entries.
const handleMissingValue = function (value) {
return { value, missing: false }
}

// List entries when using *, e.g. `a.*`
// We purposely ignore symbol properties by using `Object.keys()`.
export const getAnyEntries = function (value, path) {
const getEntries = function (value, path) {
if (Array.isArray(value)) {
return value.map((childValue, index) => ({
value: childValue,
Expand All @@ -48,3 +53,10 @@ export const getAnyEntries = function (value, path) {

return []
}

export const ANY_TOKEN = {
test,
serialize,
handleMissingValue,
getEntries,
}
26 changes: 16 additions & 10 deletions src/config/normalize/lib/star_dot_path/tokens/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
// We allow negative indexes which query from the end
// - Including -0 which can be used to append values
// Check if token is an array index integer
export const isIndexToken = function (token) {
const test = function (token) {
return Number.isInteger(token)
}

// Serialize an array index token into a string
export const serializeIndexToken = function (token) {
const serialize = function (token) {
return Object.is(token, -0) ? '-0' : String(token)
}

Expand All @@ -27,24 +27,30 @@ export const parseIndexToken = function (chars) {
return Number(chars)
}

// List entries when using indices, e.g. `a.1`
export const getIndexEntries = function (value, path, token) {
const { value: valueA, missing } = handleIndexMissingValue(value)
const index = getArrayIndex(valueA, token)
return [{ value: valueA[index], path: [...path, index], missing }]
}

// Default array when missing
export const handleIndexMissingValue = function (value) {
const handleMissingValue = function (value) {
const missing = !Array.isArray(value)
const valueA = missing ? [] : value
return { value: valueA, missing }
}

// List entries when using indices, e.g. `a.1`
const getEntries = function (value, path, token) {
const index = getArrayIndex(value, token)
return [{ value: value[index], path: [...path, index] }]
}

// Retrieve an array using a positive or negative index.
// Indices that are out-of-bound return no entries but do not error.
const getArrayIndex = function (array, token) {
return token > 0 || Object.is(token, +0)
? token
: Math.max(array.length + token, 0)
}

export const ARRAY_TOKEN = {
test,
serialize,
handleMissingValue,
getEntries,
}
12 changes: 12 additions & 0 deletions src/config/normalize/lib/star_dot_path/tokens/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ANY_TOKEN } from './any.js'
import { ARRAY_TOKEN } from './array.js'
import { PROP_TOKEN } from './prop.js'
import { REGEXP_TOKEN } from './regexp.js'

// Order is significant as they are tested serially
const TOKEN_TYPES = [ANY_TOKEN, REGEXP_TOKEN, ARRAY_TOKEN, PROP_TOKEN]

// Retrieve the type of a given token
export const getTokenType = function (token) {
return TOKEN_TYPES.find((tokenType) => tokenType.test(token))
}
24 changes: 15 additions & 9 deletions src/config/normalize/lib/star_dot_path/tokens/prop.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { isRecurseObject } from './recurse.js'
import { SEPARATOR } from './special.js'

// Check if a token is a property name string
export const isPropToken = function (token) {
const test = function (token) {
return typeof token === 'string'
}

// Serialize a property token into a string
export const serializePropToken = function (token, index) {
const serialize = function (token, index) {
return token === '' && index === 0 ? SEPARATOR : escapeSpecialChars(token)
}

Expand All @@ -17,15 +17,21 @@ export const parsePropToken = function (chars) {
return chars
}

// List entries when using property names, e.g. `a.b`
export const getPropEntries = function (value, path, token) {
const { value: valueA, missing } = handlePropMissingValue(value, token)
return [{ value: valueA[token], path: [...path, token], missing }]
}

// Default object when missing
export const handlePropMissingValue = function (value) {
const handleMissingValue = function (value) {
const missing = !isRecurseObject(value)
const valueA = missing ? {} : value
return { value: valueA, missing }
}

// List entries when using property names, e.g. `a.b`
const getEntries = function (value, path, token) {
return [{ value: value[token], path: [...path, token] }]
}

export const PROP_TOKEN = {
test,
serialize,
handleMissingValue,
getEntries,
}
18 changes: 15 additions & 3 deletions src/config/normalize/lib/star_dot_path/tokens/regexp.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { isRecurseObject } from './recurse.js'
import { REGEXP_DELIM } from './special.js'

// Check if a token is a /.../ RegExp
export const isRegExpToken = function (token) {
const test = function (token) {
return token instanceof RegExp
}

// Serialize a RegExp token into a string
export const serializeRegExpToken = function (token) {
const serialize = function (token) {
const source = escapeSpecialChars(token.source)
return `${REGEXP_DELIM}${source}${REGEXP_DELIM}${token.flags}`
}
Expand All @@ -29,8 +29,13 @@ export const parseRegExpToken = function (chars) {
return new RegExp(regExpString, regExpFlags)
}

// When missing, there are no entries, so no need to add missing entries.
const handleMissingValue = function (value) {
return { value, missing: false }
}

// List entries when using RegExps, e.g. `a./[bc]/`
export const getRegExpEntries = function (value, path, token) {
const getEntries = function (value, path, token) {
if (!isRecurseObject(value)) {
return []
}
Expand All @@ -43,3 +48,10 @@ export const getRegExpEntries = function (value, path, token) {
missing: false,
}))
}

export const REGEXP_TOKEN = {
test,
serialize,
handleMissingValue,
getEntries,
}

0 comments on commit 6a36220

Please sign in to comment.