@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
317 lines (289 loc) • 9.39 kB
JavaScript
;
const CARET_REPLACEMENTS = [
['k-i-caret-double-alt-left', 'k-i-chevron-double-left'],
['k-i-caret-double-alt-right', 'k-i-chevron-double-right'],
['k-svg-i-caret-alt-down', 'k-svg-i-chevron-down'],
['k-svg-i-caret-alt-up', 'k-svg-i-chevron-up'],
['k-svg-i-caret-alt-left', 'k-svg-i-chevron-left'],
['k-svg-i-caret-alt-right', 'k-svg-i-chevron-right'],
['k-i-caret-alt-down', 'k-i-chevron-down'],
['k-i-caret-alt-up', 'k-i-chevron-up'],
['k-i-caret-alt-left', 'k-i-chevron-left'],
['k-i-caret-alt-right', 'k-i-chevron-right'],
];
const OVERLAY_REPLACEMENTS = [
['k-overlay-light', 'k-overlay'],
];
const ALL_CLASS_REPLACEMENTS = [...CARET_REPLACEMENTS, ...OVERLAY_REPLACEMENTS];
const SCHEDULER_RE_CSS = /(\.k-scheduler\b[^{]*?|\.k-gantt\b[^{]*?)\.k-button-group\b/g;
const SCHEDULER_RE_JS = /(\.k-scheduler\b[^'"]*?|\.k-gantt\b[^'"]*?)\.k-button-group\b/g;
function applyClassReplacements(str, replacements) {
let result = str;
for (const [from, to] of replacements) {
result = result.replaceAll(from, to);
}
return result;
}
function applySchedulerSelector(str, re) {
re.lastIndex = 0;
const result = str.replace(re, (_, prefix) => prefix + '.k-segmented-control');
re.lastIndex = 0;
return result;
}
function transformStringLiterals(root, j, fn) {
let changed = false;
const visit = path => {
if (typeof path.node.value !== 'string') {
return;
}
const updated = fn(path.node.value);
if (updated === path.node.value) {
return;
}
path.node.value = updated;
if (path.node.extra) {
path.node.extra.rawValue = updated;
if (path.node.extra.raw) {
path.node.extra.raw = fn(path.node.extra.raw);
}
}
changed = true;
};
root.find(j.StringLiteral).forEach(visit);
root.find(j.Literal, n => typeof n.value === 'string').forEach(visit);
return changed;
}
function transformTemplateLiterals(root, j, fn) {
let changed = false;
root.find(j.TemplateLiteral).forEach(path => {
path.node.quasis.forEach(quasi => {
const updatedRaw = fn(quasi.value.raw);
const updatedCooked = quasi.value.cooked != null ? fn(quasi.value.cooked) : null;
if (updatedRaw !== quasi.value.raw || updatedCooked !== quasi.value.cooked) {
quasi.value.raw = updatedRaw;
if (quasi.value.cooked != null) {
quasi.value.cooked = updatedCooked;
}
changed = true;
}
});
});
return changed;
}
module.exports = {
CARET_REPLACEMENTS,
OVERLAY_REPLACEMENTS,
ALL_CLASS_REPLACEMENTS,
SCHEDULER_RE_CSS,
SCHEDULER_RE_JS,
applyClassReplacements,
applySchedulerSelector,
transformStringLiterals,
transformTemplateLiterals,
parseKendoTemplate,
buildArrowFunctionString,
buildComplexArrowFunctionString,
rewriteImplicitVars,
transformTemplateStringProperties,
};
const TEMPLATE_PROP_RE = /^(?:template|.+[Tt]emplate)$/;
function parseKendoTemplate(str) {
const SHARP = '\x00SHARP\x00';
const src = str.replace(/\\#/g, SHARP);
const tokens = [];
let i = 0;
while (i < src.length) {
const hashIdx = src.indexOf('#', i);
if (hashIdx === -1) {
if (i < src.length) {
tokens.push({ type: 'text', value: src.slice(i).replace(/\x00SHARP\x00/g, '#') });
}
break;
}
if (hashIdx > i) {
tokens.push({ type: 'text', value: src.slice(i, hashIdx).replace(/\x00SHARP\x00/g, '#') });
}
i = hashIdx + 1;
let exprType;
if (src[i] === ':') {
exprType = 'encode';
i++;
} else if (src[i] === '=') {
exprType = 'raw';
i++;
} else {
exprType = 'code';
}
const closeIdx = src.indexOf('#', i);
if (closeIdx === -1) {
return null;
}
const expr = src.slice(i, closeIdx).replace(/\x00SHARP\x00/g, '#').trim();
tokens.push({ type: exprType, value: expr });
i = closeIdx + 1;
}
return tokens;
}
const JS_KEYWORDS = new Set([
'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default',
'delete', 'do', 'else', 'export', 'extends', 'false', 'finally', 'for',
'function', 'if', 'import', 'in', 'instanceof', 'let', 'new', 'null', 'return',
'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void',
'while', 'with', 'yield', 'async', 'await', 'of'
]);
const TEMPLATE_GLOBALS = new Set([
'data', 'kendo', 'Math', 'JSON', 'console', 'window', 'document',
'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'Number', 'String',
'Boolean', 'Array', 'Object', 'Date', 'RegExp', 'Error', 'Promise',
'undefined', 'NaN', 'Infinity'
]);
function isSimpleIdent(expr) {
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(expr);
}
function rewriteImplicitVars(expr) {
return expr.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|(\b[a-zA-Z_$][a-zA-Z0-9_$]*\b)/g,
(match, ident, offset) => {
if (ident === undefined) {
return match;
}
if (JS_KEYWORDS.has(ident) || TEMPLATE_GLOBALS.has(ident)) {
return match;
}
if (offset > 0 && /[\w.]/.test(expr[offset - 1])) {
return match;
}
if (expr[offset + match.length] === '(') {
return match;
}
return `data.${ident}`;
}
);
}
function buildComplexArrowFunctionString(tokens) {
const lines = [];
let i = 0;
while (i < tokens.length) {
if (tokens[i].type === 'code') {
lines.push(rewriteImplicitVars(tokens[i].value.trim()));
i++;
} else {
const segment = [];
while (i < tokens.length && tokens[i].type !== 'code') {
segment.push(tokens[i]);
i++;
}
let content = '';
let hasContent = false;
for (const t of segment) {
if (t.type === 'text') {
content += t.value
.replace(/\\/g, '\\\\')
.replace(/`/g, '\\`')
.replace(/\$\{/g, '\\${');
if (t.value.trim()) {
hasContent = true;
}
} else {
let expr = t.value;
if (isSimpleIdent(expr)) {
expr = `data.${expr}`;
} else {
expr = rewriteImplicitVars(expr);
}
if (t.type === 'encode') {
expr = `kendo.htmlEncode(${expr})`;
}
content += `\${${expr}}`;
hasContent = true;
}
}
if (hasContent) {
lines.push(`return \`${content}\`;`);
}
}
}
return `(data) => {\n${lines.join('\n')}\n}`;
}
function buildArrowFunctionString(tokens) {
const exprTokens = tokens.filter(t => t.type !== 'text');
const hasSignificantText = tokens.some(t => t.type === 'text' && t.value.trim() !== '');
if (!hasSignificantText && exprTokens.length === 1) {
const { type, value: expr } = exprTokens[0];
const resolved = isSimpleIdent(expr) ? `data.${expr}` : rewriteImplicitVars(expr);
const body = type === 'encode' ? `kendo.htmlEncode(${resolved})` : resolved;
return `(data) => ${body}`;
}
const parts = tokens.map(token => {
if (token.type === 'text') {
return token.value
.replace(/\\/g, '\\\\')
.replace(/`/g, '\\`')
.replace(/\$\{/g, '\\${');
}
let expr = token.value;
if (token.type === 'encode') {
expr = isSimpleIdent(expr) ? `kendo.htmlEncode(data.${expr})` : `kendo.htmlEncode(${rewriteImplicitVars(expr)})`;
} else if (isSimpleIdent(expr)) {
expr = `data.${expr}`;
} else {
expr = rewriteImplicitVars(expr);
}
return `\${${expr}}`;
});
return `(data) => \`${parts.join('')}\``;
}
function transformTemplateStringProperties(root, j) {
let changed = false;
root.find(j.ObjectExpression).forEach(objPath => {
objPath.node.properties.forEach(prop => {
if (prop.type !== 'Property' && prop.type !== 'ObjectProperty') {
return;
}
const keyName = prop.key.name || prop.key.value;
if (!keyName || !TEMPLATE_PROP_RE.test(keyName)) {
return;
}
let valNode = prop.value;
const isKendoTemplateCall =
valNode.type === 'CallExpression' &&
valNode.callee.type === 'MemberExpression' &&
valNode.callee.object.name === 'kendo' &&
valNode.callee.property.name === 'template' &&
valNode.arguments.length === 1 &&
(valNode.arguments[0].type === 'StringLiteral' ||
(valNode.arguments[0].type === 'Literal' && typeof valNode.arguments[0].value === 'string'));
if (isKendoTemplateCall) {
valNode = valNode.arguments[0];
} else if (
valNode.type !== 'StringLiteral' &&
!(valNode.type === 'Literal' && typeof valNode.value === 'string')
) {
return;
}
const str = valNode.value;
const tokens = str.includes('#') ? parseKendoTemplate(str) : [{ type: 'text', value: str }];
if (!tokens) {
return;
}
let arrowFnStr;
if (tokens.some(t => t.type === 'code')) {
arrowFnStr = buildComplexArrowFunctionString(tokens);
} else {
arrowFnStr = buildArrowFunctionString(tokens);
}
if (!arrowFnStr) {
return;
}
try {
const arrowFnNode = j(`var __x = ${arrowFnStr};`)
.find(j.VariableDeclarator)
.get().node.init;
prop.value = arrowFnNode;
changed = true;
} catch (e) {
// skip
}
});
});
return changed;
}