UNPKG

@progress/kendo-ui

Version:

This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.

165 lines (141 loc) 4.7 kB
'use strict'; const { transformTemplateStringProperties, parseKendoTemplate, buildArrowFunctionString, buildComplexArrowFunctionString } = require('../_shared.cjs'); function getAttrValue(attrs, name) { const m = attrs.match(new RegExp(`(?:^|\\s)${name}\\s*=\\s*["']([^"']*)["']`, 'i')); return m ? m[1] : null; } function isKendoTemplateScript(attrs) { const type = getAttrValue(attrs, 'type'); if (type === null) { return false; } const lower = type.toLowerCase(); return lower === 'text/x-kendo-template' || lower === 'text/x-kendo-tmpl'; } function isJQuerySelectorCall(node) { return ( node.type === 'CallExpression' && node.callee.type === 'Identifier' && (node.callee.name === '$' || node.callee.name === 'jQuery') && node.arguments.length === 1 && (node.arguments[0].type === 'StringLiteral' || (node.arguments[0].type === 'Literal' && typeof node.arguments[0].value === 'string')) ); } function getIdFromJQuerySelector(node) { if (!isJQuerySelectorCall(node)) { return null; } const val = node.arguments[0].value; if (!val.startsWith('#') || val.length < 2) { return null; } if (/\s/.test(val)) { return null; } return val.slice(1); } function buildTemplateMap(source) { const map = new Map(); const re = /<script([^>]*)>([\s\S]*?)<\/script>/gi; let m; while ((m = re.exec(source)) !== null) { const attrs = m[1]; if (!isKendoTemplateScript(attrs)) { continue; } const id = getAttrValue(attrs, 'id'); if (!id) { continue; } map.set(id, m[2].trim().replace(/\r\n/g, '\n').replace(/\r/g, '\n')); } return map; } function processScriptContent(content, j, templateMap, usedIds) { let root; try { root = j(content); } catch { return content; } let changed = false; root.find(j.CallExpression, { callee: { type: 'MemberExpression', property: { name: 'html' } }, }).filter(path => { const obj = path.node.callee.object; const id = getIdFromJQuerySelector(obj); return id !== null && templateMap.has(id); }).forEach(path => { const id = getIdFromJQuerySelector(path.node.callee.object); const templateContent = templateMap.get(id); j(path).replaceWith(j.stringLiteral(templateContent)); usedIds.add(id); changed = true; }); if (!changed) {return content;} transformTemplateStringProperties(root, j); return root.toSource({ quote: 'single' }); } function removeTemplateScriptTag(source, id) { const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const re = new RegExp( `[ \\t]*<script[^>]*\\bid\\s*=\\s*["']${escaped}["'][^>]*>[\\s\\S]*?<\\/script>\\r?\\n?`, 'gi' ); return source.replace(re, ''); } function toValidIdentifier(id) { return id.replace(/[-_\s]+(.)/g, (_, ch) => ch.toUpperCase()).replace(/[^a-zA-Z0-9$]/g, '_').replace(/^([0-9])/, '_$1'); } function convertTemplateToVarDeclaration(source, id, content) { const tokens = content.includes('#') ? parseKendoTemplate(content) : [{ type: 'text', value: content }]; if (!tokens) { return source; } let arrowFnStr; if (tokens.some(t => t.type === 'code')) { arrowFnStr = buildComplexArrowFunctionString(tokens); } else { arrowFnStr = buildArrowFunctionString(tokens); } const varName = toValidIdentifier(id); const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const re = new RegExp( `[ \\t]*<script[^>]*\\bid\\s*=\\s*["']${escaped}["'][^>]*>[\\s\\S]*?<\\/script>\\r?\\n?`, 'gi' ); return source.replace(re, `<script>\nvar ${varName} = ${arrowFnStr};\n</script>\n`); } module.exports = function transformer(fileInfo, api) { if (!/\.html?$/i.test(fileInfo.path)) { return undefined; } const source = fileInfo.source; const templateMap = buildTemplateMap(source); if (templateMap.size === 0) {return undefined;} const j = api.jscodeshift; const usedIds = new Set(); let changed = false; let result = source.replace(/<script([^>]*)>([\s\S]*?)<\/script>/gi, (match, attrs, content) => { if (/\bsrc\b/.test(attrs)) {return match;} if (isKendoTemplateScript(attrs)) {return match;} const transformed = processScriptContent(content, j, templateMap, usedIds); if (transformed !== content) { changed = true; return `<script${attrs}>${transformed}</script>`; } return match; }); for (const id of usedIds) { result = removeTemplateScriptTag(result, id); } for (const [id, content] of templateMap) { if (usedIds.has(id)) { continue; } result = convertTemplateToVarDeclaration(result, id, content); changed = true; } return changed ? result : undefined; };