@taiga-ui/cdk
Version:
Base library for creating Angular components and applications using Taiga UI principles regarding of actual visual appearance
190 lines • 7.79 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.migrateRepeatTimes = migrateRepeatTimes;
const elements_1 = require("../../../../utils/templates/elements");
const template_resource_1 = require("../../../../utils/templates/template-resource");
const NGFOR_SEARCH_ATTR = '*ngFor';
const NGFOR_ATTR = '*ngFor'.toLowerCase();
const TUI_REPEAT_TIMES_DIRECTIVE_ATTR = '*tuiRepeatTimes'.toLowerCase();
const TUI_REPEAT_TIMES_DIRECTIVE_PATTERN = /^(?:let\s+(\w+)\s+)?of\s+(\S[\s\S]*)$/;
const FOR_BLOCK_PATTERN = /@for\s*\(/g;
const NGFOR_REPEAT_TIMES_PIPE_PATTERN = /let\s+(\w+)\s+of\s+([^\s|]+)\s*\|\s*tuiRepeatTimes\s*/;
function migrateRepeatTimes({ resource, recorder, fileSystem, }) {
const template = (0, template_resource_1.getTemplateFromTemplateResource)(resource, fileSystem);
const templateOffset = (0, template_resource_1.getTemplateOffset)(resource);
for (const element of (0, elements_1.findElementsWithAttribute)(template, NGFOR_SEARCH_ATTR)) {
migrateNgForRepeatTimesPipe(element, template, recorder, templateOffset);
}
for (const element of (0, elements_1.findElementsWithAttribute)(template, TUI_REPEAT_TIMES_DIRECTIVE_ATTR)) {
migrateTuiRepeatTimesDirective(element, template, recorder, templateOffset);
}
migrateAtForRepeatTimes(template, recorder, templateOffset);
}
function migrateNgForRepeatTimesPipe(element, template, recorder, offset) {
const attr = element.attrs.find((a) => a.name === NGFOR_ATTR && a.value.includes('tuiRepeatTimes'));
if (!attr) {
return;
}
const parsed = NGFOR_REPEAT_TIMES_PIPE_PATTERN.exec(attr.value);
if (!parsed) {
return;
}
const [, variable = '', expression = ''] = parsed;
const wrapped = replaceStructuralDirectiveWithForBlock({
element,
template,
recorder,
offset,
attrName: NGFOR_ATTR,
header: buildRepeatTimesForHeader({ expression }),
});
if (wrapped && variable && variable !== '_' && variable !== '$index') {
replaceVariableInElement(element, template, recorder, offset, variable);
}
}
function migrateTuiRepeatTimesDirective(element, template, recorder, offset) {
const attr = element.attrs.find((a) => a.name === TUI_REPEAT_TIMES_DIRECTIVE_ATTR);
if (!attr) {
return;
}
const parsed = TUI_REPEAT_TIMES_DIRECTIVE_PATTERN.exec(attr.value.trim());
if (!parsed) {
return;
}
const [, variable = '', expression = ''] = parsed;
replaceStructuralDirectiveWithForBlock({
element,
template,
recorder,
offset,
attrName: TUI_REPEAT_TIMES_DIRECTIVE_ATTR,
header: buildRepeatTimesForHeader({ expression, indexAliasName: variable }),
});
}
function buildRepeatTimesForHeader({ expression, indexAliasName = '', }) {
const alias = indexAliasName && indexAliasName !== '_' && indexAliasName !== '$index'
? `; let ${indexAliasName} = $index`
: '';
return `@for (_ of '-'.repeat(${expression.trim().replace(/;+$/, '')}); track $index${alias})`;
}
function replaceStructuralDirectiveWithForBlock({ element, template, recorder, offset, attrName, header, }) {
var _a;
const loc = element.sourceCodeLocation;
const attrLoc = (_a = loc === null || loc === void 0 ? void 0 : loc.attrs) === null || _a === void 0 ? void 0 : _a[attrName];
if (!loc || !attrLoc) {
return false;
}
const indentStr = ' '.repeat(computeIndent(template, loc.startOffset));
const isPureNgContainer = element.tagName === 'ng-container' && element.attrs.length === 1;
if (isPureNgContainer) {
const startTag = loc.startTag;
if (!startTag) {
return false;
}
const endTag = loc.endTag;
recorder.remove(offset + loc.startOffset, startTag.endOffset - loc.startOffset);
recorder.insertRight(offset + loc.startOffset, `${header} {`);
if (endTag) {
recorder.remove(offset + endTag.startOffset, endTag.endOffset - endTag.startOffset);
recorder.insertRight(offset + endTag.startOffset, `${indentStr}}`);
}
return true;
}
recorder.remove(offset + attrLoc.startOffset - 1, attrLoc.endOffset - attrLoc.startOffset + 1);
recorder.insertLeft(offset + loc.startOffset, `${header} {\n${indentStr}`);
recorder.insertRight(offset + loc.endOffset, `\n${indentStr}}`);
return true;
}
function replaceVariableInElement(element, template, recorder, offset, variable) {
var _a;
const loc = element.sourceCodeLocation;
if (!loc) {
return;
}
const ngForLoc = (_a = loc.attrs) === null || _a === void 0 ? void 0 : _a[NGFOR_ATTR];
const searchStart = loc.startOffset;
const searchEnd = loc.endOffset;
const content = template.slice(searchStart, searchEnd);
const pattern = new RegExp(String.raw `\b${variable}\b`, 'g');
let match;
while ((match = pattern.exec(content)) !== null) {
const absolutePos = searchStart + match.index;
if (ngForLoc &&
absolutePos >= ngForLoc.startOffset &&
absolutePos < ngForLoc.endOffset) {
continue;
}
recorder.remove(offset + absolutePos, variable.length);
recorder.insertRight(offset + absolutePos, '$index');
}
}
function computeIndent(template, pos) {
const lastNewLine = template.lastIndexOf('\n', pos);
return pos - (lastNewLine + 1);
}
function migrateAtForRepeatTimes(template, recorder, offset) {
var _a, _b;
const replacements = [];
for (const match of template.matchAll(FOR_BLOCK_PATTERN)) {
const start = match.index;
if (start === undefined) {
continue;
}
const openParen = start + ((_b = (_a = match[0]) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) - 1;
const closeParen = findMatchingParen(template, openParen);
if (closeParen === -1) {
continue;
}
const header = template.slice(openParen + 1, closeParen);
const replacementHeader = replaceRepeatTimesInForHeader(header);
if (!replacementHeader) {
continue;
}
replacements.push({
start: openParen + 1,
end: closeParen,
replacement: replacementHeader,
});
}
replacements
.sort((a, b) => b.start - a.start)
.forEach(({ start, end, replacement }) => {
recorder.remove(offset + start, end - start);
recorder.insertRight(offset + start, replacement);
});
}
function replaceRepeatTimesInForHeader(header) {
const pipeMatch = /\|\s*tuiRepeatTimes\b/.exec(header);
if ((pipeMatch === null || pipeMatch === void 0 ? void 0 : pipeMatch.index) === undefined) {
return null;
}
const beforePipe = header.slice(0, pipeMatch.index);
const ofIndex = beforePipe.lastIndexOf(' of ');
if (ofIndex === -1) {
return null;
}
const expression = beforePipe.slice(ofIndex + ' of '.length).trim();
if (!expression) {
return null;
}
const beforeExpression = beforePipe.slice(0, ofIndex + ' of '.length);
const afterPipe = header.slice(pipeMatch.index + pipeMatch[0].length);
return `${beforeExpression}'-'.repeat(${expression})${afterPipe}`;
}
function findMatchingParen(template, openParen) {
let depth = 0;
for (let i = openParen; i < template.length; i++) {
const char = template[i];
if (char === '(') {
depth++;
}
else if (char === ')') {
depth--;
if (depth === 0) {
return i;
}
}
}
return -1;
}
//# sourceMappingURL=migrate-repeat-times.js.map