UNPKG

igniteui-angular-sovn

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

273 lines (242 loc) 13.1 kB
import { Element, Text } from '@angular/compiler'; import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; import { Options } from '../../schematics/interfaces/options'; import { UpdateChanges } from '../common/UpdateChanges'; import { FileChange, findElementNodes, getAttribute, getSourceOffset, hasAttribute, parseFile } from '../common/util'; import { nativeImport } from '../common/import-helper.js'; const version = '13.0.0'; export default (options: Options): Rule => async (host: Tree, context: SchematicContext) => { context.logger.info(`Applying migration for Ignite UI for Angular to version ${version}`); const update = new UpdateChanges(__dirname, host, context); const changes = new Map<string, FileChange[]>(); const tsFiles = update.tsFiles; const SERVICES = ['IgxCsvExporterService', 'IgxExcelExporterService']; const TAGS = ['igx-grid', 'igx-tree-grid', 'igx-hierarchical-grid', 'igx-row-island']; const toolbarProp = ['[showToolbar]', 'showToolbar', '[toolbarTitle]', 'toolbarTitle', '[columnHiding]', 'columnHiding', '[columnHidingTitle]', 'columnHidingTitle', '[hiddenColumnsText]', 'hiddenColumnsText', '[columnPinning]', 'columnPinning', '[columnPinningTitle]', 'columnPinningTitle', '[pinnedColumnsText]', 'pinnedColumnsText', '[exportExcel]', 'exportExcel', '[exportExcelText]', 'exportExcelText', '[exportCsv]', 'exportCsv', '[exportCsvText]', 'exportCsvText', '[exportText]', 'exportText']; const actionsLeft = ['igx-grid-toolbar-advanced-filtering']; const { HtmlParser } = await nativeImport('@angular/compiler') as typeof import('@angular/compiler'); const moduleTsFiles = tsFiles.filter(x => x.endsWith('.module.ts')); for (const path of moduleTsFiles) { let content = host.read(path)?.toString(); const servicesInFile = []; SERVICES.forEach(service => { if (content.indexOf(service) > -1) { servicesInFile.push(service); } }); if (servicesInFile.length > 0) { let newLine = '\n'; if (content.indexOf('\r\n') > -1) { newLine = '\r\n'; } const comment = '// ' + servicesInFile.join(' and ') + ' no longer need to be manually provided and can be safely removed.' + newLine; content = comment + content; host.overwrite(path, content); } } const applyChanges = () => { for (const [path, change] of changes.entries()) { let buffer = host.read(path)?.toString(); change.sort((c, c1) => c.position - c1.position) .reverse() .forEach(c => buffer = c.apply(buffer)); host.overwrite(path, buffer); } }; const addChange = (path: string, change: FileChange) => { if (changes.has(path)) { changes.get(path).push(change); } else { changes.set(path, [change]); } }; const buildToolbar = (node, attributes) => { let result = ''; const toolbar = findElementNodes(node, ['igx-grid-toolbar'])[0]; result += `\n<igx-grid-toolbar`; const showToolbar = !toolbar || attributes['showToolbar']?.value ? attributes['showToolbar'] : getAttribute(toolbar as Element, ['*ngIf', '[*gIf]'])[0]; if (showToolbar && showToolbar.value) { result += ` *ngIf="${showToolbar.value}"`; } result += '>' const toolbarTitle = toolbar ? findElementNodes([toolbar], ['igx-grid-toolbar-title'])[0] : null; const title = !toolbarTitle || attributes['toolbarTitle']?.value ? attributes['toolbarTitle'] : (((toolbarTitle as Element).children)[0] as Text); if (title && title.value) { result += `\n<igx-grid-toolbar-title>${title.value}</igx-grid-toolbar-title>` } // has actions const hasHiding = Object.keys(attributes).filter(x => x.toLowerCase().includes('hid')).length > 0 || !!(toolbar && findElementNodes([toolbar], ['igx-grid-toolbar-hiding'])[0]); const hasPinning = Object.keys(attributes).filter(x => x.toLowerCase().includes('pin')).length > 0 || !!(toolbar && findElementNodes([toolbar], ['igx-grid-toolbar-pinning'])[0]); const hasExporting = Object.keys(attributes).filter(x => x.toLowerCase().includes('export')).length > 0 || !!(toolbar && findElementNodes([toolbar], ['igx-grid-toolbar-exporter'])[0]); const hasActions = hasHiding || hasPinning || hasExporting; if (hasActions) { result += '\n<igx-grid-toolbar-actions>'; } const hiding = toolbar ? findElementNodes([toolbar], ['igx-grid-toolbar-hiding'])[0] : null; const showHiding = !hiding || attributes['columnHiding']?.value ? attributes['columnHiding'] : getAttribute(hiding as Element, ['*ngIf', '[*ngIf]'])[0]; if (hasHiding) { result += `\n<igx-grid-toolbar-hiding`; } if (showHiding && showHiding.value) { result += `${showHiding.value !== "'true'" ? ` *ngIf="${showHiding.value}"` : ''}`; } const hidingTitle = !hiding || attributes['columnHidingTitle']?.value ? attributes['columnHidingTitle'] : getAttribute(hiding as Element, ['title', '[title]'])[0]; if (hidingTitle && hidingTitle.value) { result += ` title="${hidingTitle.value}"`; } let buttonText = !hiding || attributes['hiddenColumnsText']?.value ? attributes['hiddenColumnsText'] : getAttribute(hiding as Element, ['buttonText', '[buttonText]'])[0]; if (buttonText && buttonText.value) { result += ` buttonText="${buttonText.value}"`; } if (hasHiding) { result += '></igx-grid-toolbar-hiding>'; } const pinning = toolbar ? findElementNodes([toolbar], ['igx-grid-toolbar-pinning'])[0] : null; const showPinning = !pinning || attributes['columnPinning']?.value ? attributes['columnPinning'] : getAttribute(pinning as Element, ['*ngIf', '[*ngIf]'])[0]; if (hasPinning) { result += `\n<igx-grid-toolbar-pinning`; } if (showPinning && showPinning.value) { result += `${showPinning.value !== "'true'" ? ` *ngIf="${showPinning.value}"` : ''}`; } const pinningTitle = !pinning || attributes['columnPinningTitle']?.value ? attributes['columnPinningTitle'] : getAttribute(pinning as Element, ['title', '[title]'])[0]; if (pinningTitle && pinningTitle.value) { result += ` title="${pinningTitle.value}"`; } buttonText = !pinning || attributes['pinnedColumnsText']?.value ? attributes['pinnedColumnsText'] : getAttribute(pinning as Element, ['buttonText', '[buttonText]'])[0]; if (buttonText && buttonText.value) { result += ` buttonText="${buttonText.value}"`; } if (hasPinning) { result += '></igx-grid-toolbar-pinning>'; } const exporting = toolbar ? findElementNodes([toolbar], ['igx-grid-toolbar-exporter'])[0] : null; const showExcelExporter = !exporting || attributes['exportExcel']?.value ? attributes['exportExcel'] : getAttribute(exporting as Element, ['exportExcel', '[exportExcel]'])[0]; const showCsvExporter = !exporting || attributes['exportCsv']?.value ? attributes['exportCsv'] : getAttribute(exporting as Element, ['exportCSV', '[exportCSV]'])[0]; if (hasExporting) { result += `\n<igx-grid-toolbar-exporter`; } if (showExcelExporter && showExcelExporter.value) { result += ` exportExcel="${showExcelExporter.value}"`; } if (showCsvExporter && showCsvExporter.value) { result += ` exportCSV="${showCsvExporter.value}"`; } if (hasExporting) { result += '>'; } const excelTitle = !exporting || attributes['exportExcelText']?.value ? attributes['exportExcelText'] : getExportText(exporting, 'excelText'); if (excelTitle && excelTitle.value) { result += excelTitle.template ? '\n' + excelTitle.value : `\n<span excelText>${excelTitle.value}</span>`; } const csvTitle = !exporting || attributes['exportCsvText']?.value ? attributes['exportCsvText'] : getExportText(exporting, 'csvText'); if (csvTitle && csvTitle.value) { result += csvTitle.template ? '\n' + csvTitle.value : `\n<span csvText>${csvTitle.value}</span>`; } const exportTitle = !exporting || attributes['exportText']?.value ? attributes['exportText'] : getExportText(exporting, 'text'); if (exportTitle && exportTitle.value) { result += '\n' + exportTitle.value; } if (hasExporting) { result += '\n</igx-grid-toolbar-exporter>'; } //add any actions left if (toolbar) { const actions = findElementNodes([toolbar], actionsLeft); actions.forEach(action => { const { startTag, file, endTag } = getSourceOffset(action as Element); const text = file.content.substring(startTag.start, endTag.end); result += '\n' + text; }); } if (hasActions) { result += '\n</igx-grid-toolbar-actions>'; } const toolbarChildren = (toolbar as Element)?.children.filter(child => (child as Element).name && !(child as Element).name.includes('toolbar')); toolbarChildren?.forEach(child => { const { startTag, endTag, file } = getSourceOffset(child as Element); const replaceText = file.content.substring(startTag.start, endTag.end); result += '\n' + replaceText; }); return result + `\n</igx-grid-toolbar>`; }; const clearOldToolbar = (grid) => { const node = findElementNodes(grid, 'igx-grid-toolbar')[0]; if (!node) { return; } const { startTag, endTag, file } = getSourceOffset(node as Element); const replaceText = file.content.substring(startTag.start, endTag.end); addChange(file.url, new FileChange(startTag.start, '', replaceText, 'replace')); applyChanges(); changes.clear(); } const getExportText = (exporter, type) => { const element = exporter.children.find(el => { if (type === 'text' && !!el.value && el.value.trim().length > 0) { return el; } else if (!!el.attrs && hasAttribute(el as Element, type)) { return el; } return undefined; }); if (!element) { return ''; } if (type !== 'text') { const { startTag, endTag, file } = getSourceOffset(element as Element); const replaceText = file.content.substring(startTag.start, endTag.end); return { value: replaceText, template: true }; } return element; } for (const path of update.templateFiles) { //update toolbar const tags = TAGS.slice(0, 3); findElementNodes(parseFile(new HtmlParser(), host, path), tags) .filter(grid => hasAttribute(grid as Element, toolbarProp)) .map(node => getSourceOffset(node as Element)) .reverse() .forEach(offset => { const { startTag, file, node } = offset; const attributes = new Object(); getAttribute(node, toolbarProp).forEach(attr => attributes[attr.name.replace(/[\[\]]+/g, '')] = { name: attr.name.replace(/[\[\]]+/g, ''), value: attr.value }); const text = buildToolbar(node, attributes); clearOldToolbar(node); addChange(file.url, new FileChange(startTag.end, text)); applyChanges(); changes.clear(); }); //update row island in that file too findElementNodes(parseFile(new HtmlParser(), host, path), 'igx-row-island') .filter(grid => hasAttribute(grid as Element, toolbarProp)) .map(node => getSourceOffset(node as Element)) .reverse() .forEach(offset => { const { startTag, file, node } = offset; const attributes = new Object(); getAttribute(node, toolbarProp).forEach(attr => attributes[attr.name.replace(/[\[\]]+/g, '')] = { name: attr.name.replace(/[\[\]]+/g, ''), value: attr.value }); const text = buildToolbar(node, attributes); clearOldToolbar(node); addChange(file.url, new FileChange(startTag.end, text)); applyChanges(); changes.clear(); }); } applyChanges(); changes.clear(); update.shouldInvokeLS = options['shouldInvokeLS']; update.applyChanges(); };