UNPKG

@clr/angular

Version:

Angular components for Clarity

261 lines 11.8 kB
"use strict"; /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.TEMPLATE_MIGRATION_INLINE_CANDIDATES = exports.TEMPLATE_MIGRATION_HTML_CANDIDATES = void 0; exports.transformInlineTemplates = transformInlineTemplates; exports.migrateTemplates = migrateTemplates; exports.applyHtmlTransforms = applyHtmlTransforms; const css_replacements_1 = require("../replacements/css-replacements"); const template_replacements_1 = require("../replacements/template-replacements"); const file_visitor_1 = require("../utils/file-visitor"); const regexp_utils_1 = require("../utils/regexp-utils"); // --------------------------------------------------------------------------- // Pre-compiled regex arrays — built once at module load, not per-file // --------------------------------------------------------------------------- const COMPILED_OUTPUT_REGEXES = template_replacements_1.TEMPLATE_OUTPUT_REPLACEMENTS.map(r => ({ old: r.old, new: r.new, regex: new RegExp(`\\(${(0, regexp_utils_1.escapeRegExp)(r.old)}\\)`, 'g'), })); const COMPILED_INPUT_BOUND_REGEXES = template_replacements_1.TEMPLATE_INPUT_REPLACEMENTS.map(r => ({ old: r.old, new: r.new, boundRegex: new RegExp(`\\[${(0, regexp_utils_1.escapeRegExp)(r.old)}\\]`, 'g'), bareRegex: new RegExp(`(?<=\\s)${(0, regexp_utils_1.escapeRegExp)(r.old)}(?==)`, 'g'), })); // cds-icon [attr.*] replacements: applied only within <cds-icon> opening tags to // avoid false positives on native elements like <input [attr.size]="x">. const COMPILED_CDS_ICON_ATTR_ENTRIES = template_replacements_1.TEMPLATE_ATTRIBUTE_REPLACEMENTS.filter(r => r.context === 'cds-icon'); // Non-scoped attribute replacements: applied globally (e.g. clrPopoverAnchor). // Word boundaries (\b) prevent matching inside longer identifiers such as clrPopoverAnchorClose. const COMPILED_GLOBAL_ATTR_REGEXES = template_replacements_1.TEMPLATE_ATTRIBUTE_REPLACEMENTS.filter(r => r.context !== 'cds-icon').map(r => ({ old: r.old, new: r.new, regex: (0, regexp_utils_1.wordBoundaryRegex)(r.old), })); // Quote-aware regex that matches a complete <cds-icon …> or <cds-icon … /> opening tag. // Handles attribute values that contain > (e.g. [attr.size]="size > 0 ? 'lg' : 'md'"). const CDS_ICON_TAG_RE = /<cds-icon\b(?:[^"'/>]|"[^"]*"|'[^']*')*(?:\/?>)/g; // Opening tag of clr-datagrid only (not clr-dg-* children). const CLR_DATAGRID_OPEN_TAG_RE = /<clr-datagrid\b(?:[^"'/>]|"[^"]*"|'[^']*')*(?:\/?>)/g; // Bare attribute form: clrDgItemsTrackBy="..." (clr-datagrid host only; applied in datagrid pass). const BARE_CLR_DG_ITEMS_TRACK_BY_RE = /(?<=\s)clrDgItemsTrackBy(?==)/g; const COMPILED_HEADER_REGEXES = template_replacements_1.HEADER_CLASS_REPLACEMENTS.map(r => ({ old: r.old, new: r.new, regex: (0, regexp_utils_1.wordBoundaryRegex)(r.old), })); const COMPILED_CDS_TEXT_REGEXES = css_replacements_1.CSS_ATTRIBUTE_REPLACEMENTS.map(r => ({ old: r.old, new: r.new, regex: new RegExp((0, regexp_utils_1.escapeRegExp)(r.old), 'g'), })); // --------------------------------------------------------------------------- // Fast-path candidate strings // --------------------------------------------------------------------------- exports.TEMPLATE_MIGRATION_HTML_CANDIDATES = [ ...template_replacements_1.TEMPLATE_OUTPUT_REPLACEMENTS.map(r => r.old), ...template_replacements_1.TEMPLATE_INPUT_REPLACEMENTS.map(r => r.old), ...template_replacements_1.TEMPLATE_DATAGRID_MIGRATION_CANDIDATES, ...template_replacements_1.TEMPLATE_ATTRIBUTE_REPLACEMENTS.map(r => r.old), ...template_replacements_1.HEADER_CLASS_REPLACEMENTS.map(r => r.old), ...css_replacements_1.CSS_ATTRIBUTE_REPLACEMENTS.map(r => r.old), ]; exports.TEMPLATE_MIGRATION_INLINE_CANDIDATES = exports.TEMPLATE_MIGRATION_HTML_CANDIDATES; // --------------------------------------------------------------------------- // Public pure transforms — used by the unified .ts pass in index.ts // --------------------------------------------------------------------------- function transformInlineTemplates(text) { const templateRegex = /template\s*:\s*(`[\s\S]*?`|'[\s\S]*?')/g; return text.replace(templateRegex, (match, templateContent) => { if (!exports.TEMPLATE_MIGRATION_INLINE_CANDIDATES.some(c => templateContent.includes(c))) { return match; } const updated = applyHtmlTransforms(templateContent); return updated !== templateContent ? match.replace(templateContent, updated) : match; }); } // --------------------------------------------------------------------------- // Schematic Rule — visits .html files; .ts inline templates handled in unified pass // --------------------------------------------------------------------------- function migrateTemplates() { return (tree, context) => { context.logger.info(' Migrating HTML templates'); let htmlScanCount = 0; let htmlUpdateCount = 0; let tsScanCount = 0; let tsUpdateCount = 0; (0, file_visitor_1.visitFiles)(tree, '**/*.html', filePath => { htmlScanCount++; const content = tree.read(filePath); if (!content) { return; } const original = content.toString('utf-8'); if (!exports.TEMPLATE_MIGRATION_HTML_CANDIDATES.some(c => original.includes(c))) { return; } const updated = applyHtmlTransforms(original); if (updated !== original) { tree.overwrite(filePath, updated); htmlUpdateCount++; context.logger.info(` UPDATE ${filePath}`); } }); (0, file_visitor_1.visitFiles)(tree, '**/*.ts', filePath => { tsScanCount++; const content = tree.read(filePath); if (!content) { return; } const original = content.toString('utf-8'); if (!exports.TEMPLATE_MIGRATION_INLINE_CANDIDATES.some(c => original.includes(c))) { return; } const updated = transformInlineTemplates(original); if (updated !== original) { tree.overwrite(filePath, updated); tsUpdateCount++; context.logger.info(` UPDATE ${filePath}`); } }); const totalScanned = htmlScanCount + tsScanCount; const totalUpdated = htmlUpdateCount + tsUpdateCount; context.logger.info(` ${totalUpdated} of ${totalScanned} file(s) updated.`); }; } // --------------------------------------------------------------------------- // Helpers — exported for direct testing; not part of the public schematic API // --------------------------------------------------------------------------- function applyHtmlTransforms(text) { // Datagrid must run before migrateOutputBindings so `(clrDgSingleSelectedChange)` is still in the // source; `hadLegacySingleSelection` uses substring `clrDgSingleSelected` (matches that output name too). text = migrateClrDatagridOpeningTags(text); text = migrateOutputBindings(text); text = migrateInputBindings(text); text = migrateCdsIconAttributes(text); text = migrateHeaderClasses(text); text = migrateCdsTextAttributes(text); return text; } function migrateOutputBindings(text) { for (const r of COMPILED_OUTPUT_REGEXES) { if (!text.includes(r.old)) { continue; } r.regex.lastIndex = 0; text = text.replace(r.regex, `(${r.new})`); } return text; } // --- clr-datagrid (#2007 identity input, #2203 selection API) ---------------------------- function migrateClrDatagridOpeningTags(html) { if (!html.includes('<clr-datagrid')) { return html; } CLR_DATAGRID_OPEN_TAG_RE.lastIndex = 0; return html.replace(CLR_DATAGRID_OPEN_TAG_RE, transformClrDatagridOpeningTag); } function transformClrDatagridOpeningTag(openingTag) { const hadLegacySingleSelection = openingTag.includes('clrDgSingleSelected'); let tag = openingTag; tag = renameLegacyDatagridSingleOutputs(tag); tag = renameLegacyDatagridSingleInputs(tag); tag = renameDatagridHostIdentityFn(tag); tag = addExplicitSelectionTypeIfMissing(tag, hadLegacySingleSelection); return tag; } function renameLegacyDatagridSingleOutputs(openingTag) { return openingTag.split('(clrDgSingleSelectedChange)').join('(clrDgSelectedChange)'); } function renameLegacyDatagridSingleInputs(openingTag) { return openingTag .split('[(clrDgSingleSelected)]') .join('[(clrDgSelected)]') .replace(/\[clrDgSingleSelected\]/g, '[clrDgSelected]'); } /** Host-only: `clr-dg-items` keeps `clrDgItemsTrackBy`; this runs only on `<clr-datagrid>` tags. */ function renameDatagridHostIdentityFn(openingTag) { BARE_CLR_DG_ITEMS_TRACK_BY_RE.lastIndex = 0; return openingTag .split('[clrDgItemsTrackBy]') .join('[clrDgItemsIdentityFn]') .replace(BARE_CLR_DG_ITEMS_TRACK_BY_RE, 'clrDgItemsIdentityFn'); } /** `tag` is the opening tag after legacy single renames (so implicit multi can see `clrDgSelected`). */ function addExplicitSelectionTypeIfMissing(tag, hadLegacySingleSelection) { if (tag.includes('clrDgSelectionType')) { return tag; } if (hadLegacySingleSelection) { return appendAttributeBeforeTagClose(tag, 'clrDgSelectionType="single"'); } if (tag.includes('clrDgSelected')) { return appendAttributeBeforeTagClose(tag, 'clrDgSelectionType="multi"'); } return tag; } function appendAttributeBeforeTagClose(openingTag, attribute) { return openingTag.replace(/\s*(\/?>)\s*$/, ` ${attribute}$1`); } function migrateInputBindings(text) { for (const r of COMPILED_INPUT_BOUND_REGEXES) { if (!text.includes(r.old)) { continue; } r.boundRegex.lastIndex = 0; r.bareRegex.lastIndex = 0; text = text.replace(r.boundRegex, `[${r.new}]`); text = text.replace(r.bareRegex, r.new); } return text; } function migrateCdsIconAttributes(text) { // Scoped pass: only touch [attr.*] inside <cds-icon> opening tags. if (COMPILED_CDS_ICON_ATTR_ENTRIES.some(r => text.includes(r.old))) { CDS_ICON_TAG_RE.lastIndex = 0; text = text.replace(CDS_ICON_TAG_RE, tagMatch => { for (const { old: oldStr, new: newStr } of COMPILED_CDS_ICON_ATTR_ENTRIES) { if (tagMatch.includes(oldStr)) { tagMatch = tagMatch.split(oldStr).join(newStr); } } return tagMatch; }); } // Global pass: non-scoped attribute replacements. for (const r of COMPILED_GLOBAL_ATTR_REGEXES) { if (!text.includes(r.old)) { continue; } r.regex.lastIndex = 0; text = text.replace(r.regex, r.new); } return text; } function migrateHeaderClasses(text) { for (const r of COMPILED_HEADER_REGEXES) { if (!text.includes(r.old)) { continue; } r.regex.lastIndex = 0; text = text.replace(r.regex, r.new); } return text; } function migrateCdsTextAttributes(text) { for (const r of COMPILED_CDS_TEXT_REGEXES) { if (!text.includes(r.old)) { continue; } r.regex.lastIndex = 0; text = text.replace(r.regex, r.new); } return text; } //# sourceMappingURL=template-migration.js.map