UNPKG

@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
'use strict'; 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; }