@hsui/core
Version:
Hundsun frontend runtime core framework
269 lines (268 loc) • 6.14 kB
JavaScript
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
import _createClass from "@babel/runtime/helpers/esm/createClass";
import { isObject } from '../utils';
/**
* Path parser
* - Inspired:
* Vue.js Path parser
*/
// actions
var APPEND = 0;
var PUSH = 1;
var INC_SUB_PATH_DEPTH = 2;
var PUSH_SUB_PATH = 3;
// states
var BEFORE_PATH = 0;
var IN_PATH = 1;
var BEFORE_IDENT = 2;
var IN_IDENT = 3;
var IN_SUB_PATH = 4;
var IN_SINGLE_QUOTE = 5;
var IN_DOUBLE_QUOTE = 6;
var AFTER_PATH = 7;
var ERROR = 8;
var pathStateMachine = [];
pathStateMachine[BEFORE_PATH] = {
ws: [BEFORE_PATH],
ident: [IN_IDENT, APPEND],
'[': [IN_SUB_PATH],
eof: [AFTER_PATH]
};
pathStateMachine[IN_PATH] = {
ws: [IN_PATH],
'.': [BEFORE_IDENT],
'[': [IN_SUB_PATH],
eof: [AFTER_PATH]
};
pathStateMachine[BEFORE_IDENT] = {
ws: [BEFORE_IDENT],
ident: [IN_IDENT, APPEND],
'0': [IN_IDENT, APPEND],
number: [IN_IDENT, APPEND]
};
pathStateMachine[IN_IDENT] = {
ident: [IN_IDENT, APPEND],
'0': [IN_IDENT, APPEND],
number: [IN_IDENT, APPEND],
ws: [IN_PATH, PUSH],
'.': [BEFORE_IDENT, PUSH],
'[': [IN_SUB_PATH, PUSH],
eof: [AFTER_PATH, PUSH]
};
pathStateMachine[IN_SUB_PATH] = {
"'": [IN_SINGLE_QUOTE, APPEND],
'"': [IN_DOUBLE_QUOTE, APPEND],
'[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
']': [IN_PATH, PUSH_SUB_PATH],
eof: ERROR,
else: [IN_SUB_PATH, APPEND]
};
pathStateMachine[IN_SINGLE_QUOTE] = {
"'": [IN_SUB_PATH, APPEND],
eof: ERROR,
else: [IN_SINGLE_QUOTE, APPEND]
};
pathStateMachine[IN_DOUBLE_QUOTE] = {
'"': [IN_SUB_PATH, APPEND],
eof: ERROR,
else: [IN_DOUBLE_QUOTE, APPEND]
};
/**
* Check if an expression is a literal value.
*/
var literalValueRE = /^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/;
function isLiteral(exp) {
return literalValueRE.test(exp);
}
/**
* Strip quotes from a string
*/
function stripQuotes(str) {
var a = str.charCodeAt(0);
var b = str.charCodeAt(str.length - 1);
return a === b && (a === 0x22 || a === 0x27) ? str.slice(1, -1) : str;
}
/**
* Determine the type of a character in a keypath.
*/
function getPathCharType(ch) {
if (ch === undefined || ch === null) {
return 'eof';
}
var code = ch.charCodeAt(0);
switch (code) {
case 0x5b: // [
case 0x5d: // ]
case 0x2e: // .
case 0x22: // "
case 0x27:
// '
return ch;
case 0x5f: // _
case 0x24: // $
case 0x2d:
// -
return 'ident';
case 0x09: // Tab
case 0x0a: // Newline
case 0x0d: // Return
case 0xa0: // No-break space
case 0xfeff: // Byte Order Mark
case 0x2028: // Line Separator
case 0x2029:
// Paragraph Separator
return 'ws';
}
return 'ident';
}
/**
* Format a subPath, return its plain form if it is
* a literal string or number. Otherwise prepend the
* dynamic indicator (*).
*/
function formatSubPath(path) {
var trimmed = path.trim();
// invalid leading 0
if (path.charAt(0) === '0') {
return false;
}
return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed;
}
/**
* Parse a string path into an array of segments
*/
function parse(path) {
var keys = [];
var index = -1;
var mode = BEFORE_PATH;
var subPathDepth = 0;
var c;
var key;
var newChar;
var type;
var transition;
var action;
var typeMap;
var actions = [];
actions[PUSH] = function () {
if (key !== undefined) {
keys.push(key);
key = undefined;
}
};
actions[APPEND] = function () {
if (key === undefined) {
key = newChar;
} else {
key += newChar;
}
};
actions[INC_SUB_PATH_DEPTH] = function () {
actions[APPEND]();
subPathDepth++;
};
actions[PUSH_SUB_PATH] = function () {
if (subPathDepth > 0) {
subPathDepth--;
mode = IN_SUB_PATH;
actions[APPEND]();
} else {
subPathDepth = 0;
if (key === undefined) {
return false;
}
key = formatSubPath(key);
if (key === false) {
return false;
} else {
actions[PUSH]();
}
}
};
function maybeUnescapeQuote() {
var nextChar = path[index + 1];
if (mode === IN_SINGLE_QUOTE && nextChar === "'" || mode === IN_DOUBLE_QUOTE && nextChar === '"') {
index++;
newChar = '\\' + nextChar;
actions[APPEND]();
return true;
}
}
while (mode !== null) {
index++;
c = path[index];
if (c === '\\' && maybeUnescapeQuote()) {
continue;
}
type = getPathCharType(c);
typeMap = pathStateMachine[mode];
transition = typeMap[type] || typeMap['else'] || ERROR;
if (transition === ERROR) {
return; // parse error
}
mode = transition[0];
action = actions[transition[1]];
if (action) {
newChar = transition[2];
newChar = newChar === undefined ? c : newChar;
if (action() === false) {
return;
}
}
if (mode === AFTER_PATH) {
return keys;
}
}
}
var I18nPath = /*#__PURE__*/function () {
function I18nPath() {
_classCallCheck(this, I18nPath);
this._cache = void 0;
this._cache = Object.create(null);
}
/**
* External parse that check for a cache hit first
*/
_createClass(I18nPath, [{
key: "parsePath",
value: function parsePath(path) {
var hit = this._cache[path];
if (!hit) {
hit = parse(path);
if (hit) {
this._cache[path] = hit;
}
}
return hit || [];
}
/**
* Get path value from path string
*/
}, {
key: "getPathValue",
value: function getPathValue(obj, path) {
if (!isObject(obj)) {
return null;
}
var paths = this.parsePath(path);
if (paths.length === 0) {
return null;
} else {
var length = paths.length;
var last = obj;
var i = 0;
while (i < length) {
var value = last[paths[i]];
if (value === undefined || value === null) {
return null;
}
last = value;
i++;
}
return last;
}
}
}]);
return I18nPath;
}();
export { I18nPath as default };