@koobiq/components 20.1.0 → 20.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/accordion/_accordion-theme.scss +6 -2
- package/accordion/accordion-tokens.scss +3 -0
- package/accordion/{accordion-trigger.component.scss → accordion-trigger.scss} +10 -0
- package/accordion/accordion.scss +71 -0
- package/accordion/index.d.ts +264 -121
- package/actions-panel/index.d.ts +25 -13
- package/alert/_alert-theme.scss +14 -0
- package/alert/alert.component.scss +24 -13
- package/alert/index.d.ts +63 -29
- package/app-switcher/app-switcher-dropdown-app.scss +7 -5
- package/app-switcher/app-switcher-dropdown-site.scss +6 -4
- package/app-switcher/index.d.ts +41 -4
- package/app-switcher/kbq-app-switcher-list-item.scss +6 -8
- package/autocomplete/autocomplete.scss +2 -1
- package/autocomplete/index.d.ts +58 -37
- package/badge/index.d.ts +2 -3
- package/breadcrumbs/index.d.ts +1 -1
- package/button/index.d.ts +2 -2
- package/button-toggle/button-toggle-tokens.scss +2 -0
- package/button-toggle/button-toggle.scss +9 -0
- package/button-toggle/index.d.ts +5 -0
- package/checkbox/index.d.ts +33 -29
- package/clamped-text/index.d.ts +3 -2
- package/code-block/index.d.ts +24 -19
- package/content-panel/index.d.ts +1 -1
- package/core/index.d.ts +1217 -186
- package/core/option/_action-container-mixins.scss +28 -0
- package/core/option/_option-theme.scss +24 -0
- package/core/option/action.scss +7 -0
- package/core/styles/_core.scss +2 -0
- package/core/styles/_koobiq-theme.scss +2 -0
- package/core/styles/_tokens.scss +1 -0
- package/core/styles/common/_animation.scss +31 -24
- package/core/styles/common/_connected-overlay.scss +39 -0
- package/core/styles/common/_select.scss +19 -2
- package/core/styles/common/_tokens.scss +0 -9
- package/core/styles/theming/_index.scss +0 -1
- package/core/styles/theming/_theming.scss +34 -5
- package/datepicker/index.d.ts +46 -15
- package/dl/dl.scss +26 -0
- package/dl/index.d.ts +8 -1
- package/dropdown/_dropdown-theme.scss +28 -2
- package/dropdown/dropdown-item.scss +29 -0
- package/dropdown/dropdown-tokens.scss +2 -1
- package/dropdown/dropdown.scss +16 -1
- package/dropdown/index.d.ts +331 -17
- package/dynamic-translation/index.d.ts +1 -1
- package/fesm2022/koobiq-components-accordion.mjs +480 -317
- package/fesm2022/koobiq-components-accordion.mjs.map +1 -1
- package/fesm2022/koobiq-components-actions-panel.mjs +57 -46
- package/fesm2022/koobiq-components-actions-panel.mjs.map +1 -1
- package/fesm2022/koobiq-components-alert.mjs +98 -58
- package/fesm2022/koobiq-components-alert.mjs.map +1 -1
- package/fesm2022/koobiq-components-app-switcher.mjs +233 -38
- package/fesm2022/koobiq-components-app-switcher.mjs.map +1 -1
- package/fesm2022/koobiq-components-autocomplete.mjs +89 -52
- package/fesm2022/koobiq-components-autocomplete.mjs.map +1 -1
- package/fesm2022/koobiq-components-badge.mjs +18 -17
- package/fesm2022/koobiq-components-badge.mjs.map +1 -1
- package/fesm2022/koobiq-components-breadcrumbs.mjs +25 -25
- package/fesm2022/koobiq-components-breadcrumbs.mjs.map +1 -1
- package/fesm2022/koobiq-components-button-toggle.mjs +17 -12
- package/fesm2022/koobiq-components-button-toggle.mjs.map +1 -1
- package/fesm2022/koobiq-components-button.mjs +25 -25
- package/fesm2022/koobiq-components-checkbox.mjs +69 -92
- package/fesm2022/koobiq-components-checkbox.mjs.map +1 -1
- package/fesm2022/koobiq-components-clamped-text.mjs +18 -13
- package/fesm2022/koobiq-components-clamped-text.mjs.map +1 -1
- package/fesm2022/koobiq-components-code-block.mjs +56 -69
- package/fesm2022/koobiq-components-code-block.mjs.map +1 -1
- package/fesm2022/koobiq-components-content-panel.mjs +29 -29
- package/fesm2022/koobiq-components-core.mjs +2485 -735
- package/fesm2022/koobiq-components-core.mjs.map +1 -1
- package/fesm2022/koobiq-components-datepicker.mjs +168 -126
- package/fesm2022/koobiq-components-datepicker.mjs.map +1 -1
- package/fesm2022/koobiq-components-divider.mjs +7 -7
- package/fesm2022/koobiq-components-dl.mjs +23 -15
- package/fesm2022/koobiq-components-dl.mjs.map +1 -1
- package/fesm2022/koobiq-components-dropdown.mjs +814 -136
- package/fesm2022/koobiq-components-dropdown.mjs.map +1 -1
- package/fesm2022/koobiq-components-dynamic-translation.mjs +13 -13
- package/fesm2022/koobiq-components-ellipsis-center.mjs +7 -7
- package/fesm2022/koobiq-components-empty-state.mjs +19 -19
- package/fesm2022/koobiq-components-file-upload.mjs +74 -42
- package/fesm2022/koobiq-components-file-upload.mjs.map +1 -1
- package/fesm2022/koobiq-components-filter-bar.mjs +783 -199
- package/fesm2022/koobiq-components-filter-bar.mjs.map +1 -1
- package/fesm2022/koobiq-components-flag.mjs +7 -7
- package/fesm2022/koobiq-components-form-field.mjs +350 -162
- package/fesm2022/koobiq-components-form-field.mjs.map +1 -1
- package/fesm2022/koobiq-components-icon.mjs +52 -25
- package/fesm2022/koobiq-components-icon.mjs.map +1 -1
- package/fesm2022/koobiq-components-inline-edit.mjs +175 -28
- package/fesm2022/koobiq-components-inline-edit.mjs.map +1 -1
- package/fesm2022/koobiq-components-input.mjs +41 -24
- package/fesm2022/koobiq-components-input.mjs.map +1 -1
- package/fesm2022/koobiq-components-link.mjs +8 -8
- package/fesm2022/koobiq-components-list.mjs +657 -143
- package/fesm2022/koobiq-components-list.mjs.map +1 -1
- package/fesm2022/koobiq-components-loader-overlay.mjs +16 -16
- package/fesm2022/koobiq-components-markdown.mjs +14 -11
- package/fesm2022/koobiq-components-markdown.mjs.map +1 -1
- package/fesm2022/koobiq-components-modal.mjs +59 -35
- package/fesm2022/koobiq-components-modal.mjs.map +1 -1
- package/fesm2022/koobiq-components-navbar.mjs +177 -67
- package/fesm2022/koobiq-components-navbar.mjs.map +1 -1
- package/fesm2022/koobiq-components-notification-center.mjs +40 -24
- package/fesm2022/koobiq-components-notification-center.mjs.map +1 -1
- package/fesm2022/koobiq-components-overflow-items.mjs +16 -17
- package/fesm2022/koobiq-components-overflow-items.mjs.map +1 -1
- package/fesm2022/koobiq-components-popover.mjs +31 -21
- package/fesm2022/koobiq-components-popover.mjs.map +1 -1
- package/fesm2022/koobiq-components-progress-bar.mjs +13 -13
- package/fesm2022/koobiq-components-progress-spinner.mjs +13 -13
- package/fesm2022/koobiq-components-radio.mjs +10 -10
- package/fesm2022/koobiq-components-resizer.mjs +33 -14
- package/fesm2022/koobiq-components-resizer.mjs.map +1 -1
- package/fesm2022/koobiq-components-scrollbar.mjs +10 -10
- package/fesm2022/koobiq-components-search-expandable.mjs +49 -14
- package/fesm2022/koobiq-components-search-expandable.mjs.map +1 -1
- package/fesm2022/koobiq-components-select.mjs +450 -146
- package/fesm2022/koobiq-components-select.mjs.map +1 -1
- package/fesm2022/koobiq-components-sidebar.mjs +14 -14
- package/fesm2022/koobiq-components-sidepanel.mjs +26 -26
- package/fesm2022/koobiq-components-skeleton.mjs +10 -10
- package/fesm2022/koobiq-components-split-button.mjs +10 -17
- package/fesm2022/koobiq-components-split-button.mjs.map +1 -1
- package/fesm2022/koobiq-components-splitter.mjs +17 -17
- package/fesm2022/koobiq-components-table.mjs +15 -13
- package/fesm2022/koobiq-components-table.mjs.map +1 -1
- package/fesm2022/koobiq-components-tabs.mjs +80 -68
- package/fesm2022/koobiq-components-tabs.mjs.map +1 -1
- package/fesm2022/koobiq-components-tags.mjs +283 -122
- package/fesm2022/koobiq-components-tags.mjs.map +1 -1
- package/fesm2022/koobiq-components-textarea.mjs +13 -8
- package/fesm2022/koobiq-components-textarea.mjs.map +1 -1
- package/fesm2022/koobiq-components-time-range.mjs +29 -24
- package/fesm2022/koobiq-components-time-range.mjs.map +1 -1
- package/fesm2022/koobiq-components-timepicker.mjs +61 -30
- package/fesm2022/koobiq-components-timepicker.mjs.map +1 -1
- package/fesm2022/koobiq-components-timezone.mjs +77 -42
- package/fesm2022/koobiq-components-timezone.mjs.map +1 -1
- package/fesm2022/koobiq-components-title.mjs +7 -7
- package/fesm2022/koobiq-components-toast.mjs +51 -26
- package/fesm2022/koobiq-components-toast.mjs.map +1 -1
- package/fesm2022/koobiq-components-toggle.mjs +72 -64
- package/fesm2022/koobiq-components-toggle.mjs.map +1 -1
- package/fesm2022/koobiq-components-tooltip.mjs +219 -28
- package/fesm2022/koobiq-components-tooltip.mjs.map +1 -1
- package/fesm2022/koobiq-components-top-bar.mjs +13 -13
- package/fesm2022/koobiq-components-tree-select.mjs +222 -80
- package/fesm2022/koobiq-components-tree-select.mjs.map +1 -1
- package/fesm2022/koobiq-components-tree.mjs +302 -81
- package/fesm2022/koobiq-components-tree.mjs.map +1 -1
- package/fesm2022/koobiq-components-username.mjs +27 -27
- package/file-upload/index.d.ts +25 -6
- package/file-upload/multiple-file-upload.component.scss +14 -0
- package/filter-bar/_filter-bar-theme.scss +0 -10
- package/filter-bar/filter-bar-tokens.scss +3 -0
- package/filter-bar/filters.scss +3 -2
- package/filter-bar/index.d.ts +281 -9
- package/filter-bar/pipes/base-pipe.scss +0 -11
- package/filter-bar/pipes/pipe-input.scss +10 -0
- package/filter-bar/pipes/pipe-multi-tree-select.scss +0 -6
- package/filter-bar/pipes/pipe-multiselect.scss +0 -6
- package/filter-bar/pipes/pipe-tree-select.scss +0 -6
- package/form-field/_form-field-theme.scss +76 -20
- package/form-field/fieldset.scss +10 -0
- package/form-field/form-field-tokens.scss +15 -1
- package/form-field/form-field.scss +15 -19
- package/form-field/index.d.ts +147 -65
- package/icon/icon-button.scss +3 -1
- package/icon/index.d.ts +45 -7
- package/inline-edit/index.d.ts +57 -4
- package/inline-edit/inline-edit.scss +10 -0
- package/input/index.d.ts +17 -2
- package/list/_list-theme.scss +42 -10
- package/list/index.d.ts +326 -79
- package/list/list-tokens.scss +43 -8
- package/list/list.scss +186 -12
- package/markdown/index.d.ts +1 -1
- package/modal/index.d.ts +21 -4
- package/modal/modal-tokens.scss +1 -1
- package/navbar/_navbar-item_vertical.scss +7 -5
- package/navbar/index.d.ts +54 -5
- package/navbar/navbar-brand.scss +23 -7
- package/navbar/vertical-navbar.scss +6 -0
- package/notification-center/index.d.ts +2 -1
- package/notification-center/notification-item.scss +6 -0
- package/package.json +3 -6
- package/popover/index.d.ts +2 -2
- package/prebuilt-themes/dark-theme.css +1 -1
- package/prebuilt-themes/light-theme.css +1 -1
- package/prebuilt-themes/theme.css +1 -1
- package/resizer/index.d.ts +12 -0
- package/schematics/collection.json +10 -0
- package/schematics/migrations/alert-signals/README.md +64 -0
- package/schematics/migrations/alert-signals/data.js +70 -0
- package/schematics/migrations/alert-signals/index.js +350 -0
- package/schematics/migrations/alert-signals/schema.json +20 -0
- package/schematics/migrations/autocomplete-panel-width-auto/README.md +68 -0
- package/schematics/migrations/autocomplete-panel-width-auto/index.js +70 -0
- package/schematics/migrations/autocomplete-panel-width-auto/schema.json +15 -0
- package/schematics/migrations/deprecated-icons/README.md +29 -4
- package/schematics/migrations/deprecated-icons/data.js +7 -0
- package/schematics/migrations/deprecated-icons/index.js +62 -35
- package/schematics/migrations/filter-bar-signals/README.md +1 -1
- package/schematics/migrations/new-icons-pack/README.md +37 -2
- package/schematics/migrations/new-icons-pack/data.js +8 -0
- package/schematics/migrations/new-icons-pack/index.js +144 -53
- package/schematics/migrations.json +11 -1
- package/schematics/ng-add/index.js +3 -3
- package/schematics/utils/angular-parsing.js +41 -14
- package/schematics/utils/ast.js +31 -0
- package/schematics/utils/icon-migration.js +530 -0
- package/schematics/utils/typescript.js +44 -0
- package/scrollbar/index.d.ts +1 -1
- package/search-expandable/index.d.ts +3 -0
- package/select/index.d.ts +212 -40
- package/select/select-tokens.scss +0 -1
- package/select/select.scss +0 -2
- package/splitter/index.d.ts +1 -1
- package/table/_table-theme.scss +4 -0
- package/table/index.d.ts +2 -1
- package/table/table.scss +8 -0
- package/tabs/_tabs-common.scss +60 -31
- package/tabs/_tabs-theme.scss +12 -0
- package/tabs/index.d.ts +2 -2
- package/tabs/tab-header.scss +14 -0
- package/tabs/tab-nav-bar.scss +4 -0
- package/tabs/tabs-tokens.scss +1 -0
- package/tags/index.d.ts +128 -29
- package/textarea/index.d.ts +5 -0
- package/time-range/index.d.ts +1 -1
- package/timepicker/index.d.ts +4 -0
- package/timezone/index.d.ts +10 -8
- package/toast/index.d.ts +26 -3
- package/toggle/index.d.ts +34 -14
- package/tooltip/index.d.ts +119 -7
- package/tree/_tree-theme.scss +25 -5
- package/tree/index.d.ts +71 -3
- package/tree/tree-option.scss +2 -8
- package/tree-select/_tree-select-theme.scss +22 -0
- package/tree-select/index.d.ts +127 -36
- package/accordion/accordion.component.scss +0 -50
- package/core/styles/_tokens.kbq.import.scss +0 -4
- package/core/styles/theming/_components-theming.scss +0 -1927
- package/filter-bar/pipes/pipe-select.scss +0 -5
- package/overflow-items/overflow-items.scss +0 -20
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ts = require('typescript');
|
|
4
|
+
var utils_ast = require('./ast.js');
|
|
5
|
+
var utils_typescript = require('./typescript.js');
|
|
6
|
+
|
|
7
|
+
function escapeRegExp(value) {
|
|
8
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Builds the exact-match token table shared by every exact-token-only path (class lists,
|
|
12
|
+
* `[class.X]` bindings, bare TS string literals, style-file regex): the bare scope word (mapped
|
|
13
|
+
* per `scopeClassInList`) plus, for every icon, `<prefix>-<icon.from> -> scope.to-<icon.to>` under
|
|
14
|
+
* *both* the old scope prefix and the new one — a project may already have run the prefix half of
|
|
15
|
+
* a migration (`mc-<name>` -> `kbq-<name>`) without yet getting the name rename, so both starting
|
|
16
|
+
* points have to resolve to the same, final `scope.to`-prefixed value. A pair that resolves to
|
|
17
|
+
* itself (already fully migrated) is never added, so it can't be reported/rewritten as a spurious
|
|
18
|
+
* no-op change.
|
|
19
|
+
*/
|
|
20
|
+
function buildIconTokenMap(data) {
|
|
21
|
+
const map = new Map();
|
|
22
|
+
const prefixes = Array.from(new Set([data.scope.from, data.scope.to]));
|
|
23
|
+
const setIfChanged = (key, value) => {
|
|
24
|
+
if (key !== value) {
|
|
25
|
+
map.set(key, value);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
setIfChanged(data.scope.from, data.scopeClassInList === 'remove' ? null : data.scope.to);
|
|
29
|
+
for (const icon of data.icons) {
|
|
30
|
+
for (const prefix of prefixes) {
|
|
31
|
+
setIfChanged(`${prefix}-${icon.from}`, `${data.scope.to}-${icon.to}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return map;
|
|
35
|
+
}
|
|
36
|
+
function exactTokenLookup(tokenMap, found) {
|
|
37
|
+
return (token) => {
|
|
38
|
+
if (!tokenMap.has(token)) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const replacement = tokenMap.get(token);
|
|
42
|
+
found.push({ from: token, to: replacement ?? '' });
|
|
43
|
+
return replacement;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* HTML-AST visitor, element-name-agnostic (unlike `ElementCollector`'s single `elementName`),
|
|
48
|
+
* since icon directives attach via attribute selector to any host element. Collects every
|
|
49
|
+
* element carrying at least one relevant attribute — a `valueAttrs` match, a bare `class`
|
|
50
|
+
* attribute, or a `class.X` binding.
|
|
51
|
+
*/
|
|
52
|
+
class IconAttributeCollector {
|
|
53
|
+
constructor(data) {
|
|
54
|
+
this.data = data;
|
|
55
|
+
this.elementsToMigrate = [];
|
|
56
|
+
}
|
|
57
|
+
visitElement(el) {
|
|
58
|
+
if (el.attrs?.length) {
|
|
59
|
+
const isRelevant = el.attrs.some((attr) => {
|
|
60
|
+
const name = utils_ast.getSimpleAttributeName(attr.name);
|
|
61
|
+
return (this.data.valueAttrs.includes(name) || name === 'class' || utils_ast.getClassBindingToken(name) !== undefined);
|
|
62
|
+
});
|
|
63
|
+
if (isRelevant) {
|
|
64
|
+
this.elementsToMigrate.push(el);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
this.visitChildren(el);
|
|
68
|
+
}
|
|
69
|
+
visitChildren(el) {
|
|
70
|
+
if (el.children?.length) {
|
|
71
|
+
for (const child of el.children) {
|
|
72
|
+
child.visit(this);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
visitBlock(block, _1) {
|
|
77
|
+
this.visitChildren(block);
|
|
78
|
+
}
|
|
79
|
+
// ICU expansions (`{count, plural, =1 {...} other {...}}`) hold their child nodes on
|
|
80
|
+
// `cases[].expression`, not `children`, so `visitChildren` never reaches them on its own.
|
|
81
|
+
visitExpansion(expansion, _1) {
|
|
82
|
+
utils_ast.visitAll(this, expansion.cases);
|
|
83
|
+
}
|
|
84
|
+
visitExpansionCase(expansionCase, _1) {
|
|
85
|
+
utils_ast.visitAll(this, expansionCase.expression);
|
|
86
|
+
}
|
|
87
|
+
// part of interface
|
|
88
|
+
visitAttribute(_, _1) { }
|
|
89
|
+
visitText(_, _1) { }
|
|
90
|
+
visitComment(_, _1) { }
|
|
91
|
+
visitBlockParameter(_, _1) { }
|
|
92
|
+
visitLetDeclaration(_, _1) { }
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* If `value` (already trimmed) is a single quoted string literal — starts and ends with the same
|
|
96
|
+
* quote character, with no further occurrence of that character in between — returns the quote
|
|
97
|
+
* used and the unquoted content. Returns `undefined` for anything else, including a concatenation
|
|
98
|
+
* like `'mc-' + name` (which merely starts and ends with a quote).
|
|
99
|
+
*/
|
|
100
|
+
function extractQuotedLiteral(value) {
|
|
101
|
+
const trimmed = value.trim();
|
|
102
|
+
if (trimmed.length < 2) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
const quote = trimmed[0];
|
|
106
|
+
if ((quote !== "'" && quote !== '"') || trimmed[trimmed.length - 1] !== quote) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
const inner = trimmed.slice(1, -1);
|
|
110
|
+
if (inner.includes(quote)) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
return { quote, content: inner };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Resolves a bare (non-template) string literal like `'pt-icons word-wrap_16'` or
|
|
117
|
+
* `'pt-icons-word-wrap_16'`, operating on its *raw* source text (escape sequences intact, never
|
|
118
|
+
* decoded) so the replacement can be spliced back in without corrupting them. Only proceeds when
|
|
119
|
+
* the literal contains an unambiguous icon reference — a hyphenated `scope-suffix` token that's a
|
|
120
|
+
* known replacement, or a bare icon suffix *alongside* the scope word elsewhere in the same
|
|
121
|
+
* literal — so a string that merely contains the bare scope word (e.g. `'pt-icons my-custom-thing'`,
|
|
122
|
+
* where `my-custom-thing` isn't a known icon) is left untouched, and a completely unscoped bare
|
|
123
|
+
* suffix (e.g. `'add-to-list_16'` with no scope word anywhere) is never rewritten on its own.
|
|
124
|
+
*/
|
|
125
|
+
function resolveBareStringLiteral(rawText, tokenMap, data) {
|
|
126
|
+
const tokens = rawText.split(/\s+/).filter(Boolean);
|
|
127
|
+
const hasScopeWord = tokens.includes(data.scope.from) || tokens.includes(data.scope.to);
|
|
128
|
+
const hasPrefixedIconMatch = tokens.some((token) => tokenMap.has(token) && token !== data.scope.from && token !== data.scope.to);
|
|
129
|
+
const hasBareIconMatch = hasScopeWord && tokens.some((token) => data.icons.some(({ from }) => from === token));
|
|
130
|
+
if (!hasPrefixedIconMatch && !hasBareIconMatch) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
const found = [];
|
|
134
|
+
let changed = false;
|
|
135
|
+
const resolved = tokens
|
|
136
|
+
.map((token) => {
|
|
137
|
+
if (tokenMap.has(token)) {
|
|
138
|
+
const replacement = tokenMap.get(token);
|
|
139
|
+
changed = true;
|
|
140
|
+
found.push({ from: token, to: replacement ?? '' });
|
|
141
|
+
return replacement;
|
|
142
|
+
}
|
|
143
|
+
const icon = data.icons.find(({ from }) => from === token);
|
|
144
|
+
if (icon && icon.to !== token) {
|
|
145
|
+
changed = true;
|
|
146
|
+
found.push({ from: token, to: icon.to });
|
|
147
|
+
return icon.to;
|
|
148
|
+
}
|
|
149
|
+
return token;
|
|
150
|
+
})
|
|
151
|
+
.filter((token) => !!token);
|
|
152
|
+
return changed ? { result: resolved.join(' '), found } : undefined;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Template-level transform pluggable into `migrateTemplateWithTransform`. Walks the HTML AST via
|
|
156
|
+
* `IconAttributeCollector` and, for each matched element:
|
|
157
|
+
* - `valueAttrs` match: a bound attribute (`[kbq-icon]="expr"`) whose value isn't a single quoted
|
|
158
|
+
* literal (e.g. a bare identifier, or a concatenation like `'mc-' + name`) is a genuine runtime
|
|
159
|
+
* expression and is warned about, never mutated — same for any value containing `{{ }}`
|
|
160
|
+
* interpolation, bound or not, since it can't be resolved statically either. A quoted literal's
|
|
161
|
+
* replacement is re-wrapped in the same quote character so the result stays a valid Angular
|
|
162
|
+
* expression; a plain attribute's value is spliced in as-is.
|
|
163
|
+
* - `class` attribute: only when it's a plain (unbound) attribute — only exact-matching tokens in
|
|
164
|
+
* the space-separated list are replaced or dropped; unrelated classes, and any `[class]`
|
|
165
|
+
* binding expression, are left untouched.
|
|
166
|
+
* - `class.X` binding: `X` is matched exactly against `tokenMap`. A rename splices just the
|
|
167
|
+
* attribute's name (the `X` part); a token that maps to removal drops the whole attribute
|
|
168
|
+
* (name, value and `=`) since there's no value left to bind.
|
|
169
|
+
* When `fix` is false, performs the same walk without mutating and reports matches via `onFound`.
|
|
170
|
+
*/
|
|
171
|
+
function createIconTemplateTransform(data, tokenMap, opts) {
|
|
172
|
+
return async (template, fileName) => {
|
|
173
|
+
const parsed = await utils_typescript.parseTemplate(template);
|
|
174
|
+
if (parsed.tree === undefined) {
|
|
175
|
+
return { fileContent: template, changed: undefined, errors: parsed.errors };
|
|
176
|
+
}
|
|
177
|
+
const visitor = new IconAttributeCollector(data);
|
|
178
|
+
utils_ast.visitAll(visitor, parsed.tree.rootNodes);
|
|
179
|
+
const edits = [];
|
|
180
|
+
const allFound = [];
|
|
181
|
+
const resolveValueToken = (token) => {
|
|
182
|
+
if (tokenMap.has(token)) {
|
|
183
|
+
return tokenMap.get(token);
|
|
184
|
+
}
|
|
185
|
+
if (data.rescopeUnknownValues && token.startsWith(`${data.scope.from}-`)) {
|
|
186
|
+
return `${data.scope.to}-${token.slice(data.scope.from.length + 1)}`;
|
|
187
|
+
}
|
|
188
|
+
return undefined;
|
|
189
|
+
};
|
|
190
|
+
const warn = (message) => {
|
|
191
|
+
opts.warn(`${message}${(fileName && ' File: ' + fileName) || ''}`);
|
|
192
|
+
};
|
|
193
|
+
for (const el of visitor.elementsToMigrate) {
|
|
194
|
+
for (const attr of el.attrs) {
|
|
195
|
+
if (!attr.valueSpan) {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const simpleName = utils_ast.getSimpleAttributeName(attr.name);
|
|
199
|
+
if (data.valueAttrs.includes(simpleName)) {
|
|
200
|
+
if (attr.value.includes('{{')) {
|
|
201
|
+
warn('Element is using an interpolated value. Check the code and change value on your own.');
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const isBound = attr.name.startsWith('[');
|
|
205
|
+
const literal = extractQuotedLiteral(attr.value);
|
|
206
|
+
const isDynamicExpression = isBound && literal === undefined;
|
|
207
|
+
if (isDynamicExpression) {
|
|
208
|
+
warn('Element is using a dynamic value. Check the code and change value on your own.');
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const cleanValue = literal ? literal.content : attr.value;
|
|
212
|
+
const resolved = resolveValueToken(cleanValue);
|
|
213
|
+
if (resolved) {
|
|
214
|
+
const replacementText = literal ? `${literal.quote}${resolved}${literal.quote}` : resolved;
|
|
215
|
+
edits.push({
|
|
216
|
+
start: attr.valueSpan.start.offset,
|
|
217
|
+
end: attr.valueSpan.end.offset,
|
|
218
|
+
replacement: replacementText
|
|
219
|
+
});
|
|
220
|
+
allFound.push({ from: cleanValue, to: resolved });
|
|
221
|
+
}
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (simpleName === 'class' && !attr.name.startsWith('[')) {
|
|
225
|
+
const found = [];
|
|
226
|
+
const newValue = utils_ast.replaceTokenList(attr.value, exactTokenLookup(tokenMap, found));
|
|
227
|
+
if (newValue !== undefined) {
|
|
228
|
+
edits.push({
|
|
229
|
+
start: attr.valueSpan.start.offset,
|
|
230
|
+
end: attr.valueSpan.end.offset,
|
|
231
|
+
replacement: newValue
|
|
232
|
+
});
|
|
233
|
+
allFound.push(...found);
|
|
234
|
+
}
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const bindingToken = utils_ast.getClassBindingToken(simpleName);
|
|
238
|
+
if (bindingToken !== undefined && tokenMap.has(bindingToken)) {
|
|
239
|
+
const replacement = tokenMap.get(bindingToken);
|
|
240
|
+
if (replacement) {
|
|
241
|
+
const newSimpleName = simpleName.replace(new RegExp(`\\.${escapeRegExp(bindingToken)}$`), `.${replacement}`);
|
|
242
|
+
const newName = attr.name.startsWith('[') ? `[${newSimpleName}]` : newSimpleName;
|
|
243
|
+
edits.push({
|
|
244
|
+
start: attr.keySpan.start.offset,
|
|
245
|
+
end: attr.keySpan.end.offset,
|
|
246
|
+
replacement: newName
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
// No replacement — the binding can't be renamed in place, and there's no
|
|
251
|
+
// value left worth keeping either, so drop the whole attribute.
|
|
252
|
+
edits.push({
|
|
253
|
+
start: attr.sourceSpan.start.offset,
|
|
254
|
+
end: attr.sourceSpan.end.offset,
|
|
255
|
+
replacement: ''
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
allFound.push({ from: bindingToken, to: replacement ?? '' });
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (!opts.fix) {
|
|
263
|
+
if (allFound.length > 0) {
|
|
264
|
+
opts.onFound(fileName, allFound);
|
|
265
|
+
}
|
|
266
|
+
return { fileContent: template, changed: false, errors: parsed.errors };
|
|
267
|
+
}
|
|
268
|
+
if (edits.length === 0) {
|
|
269
|
+
return { fileContent: template, changed: false, errors: parsed.errors };
|
|
270
|
+
}
|
|
271
|
+
edits.sort((a, b) => a.start - b.start);
|
|
272
|
+
let updatedTemplate = template;
|
|
273
|
+
let offset = 0;
|
|
274
|
+
for (const edit of edits) {
|
|
275
|
+
updatedTemplate =
|
|
276
|
+
updatedTemplate.slice(0, edit.start - offset) +
|
|
277
|
+
edit.replacement +
|
|
278
|
+
updatedTemplate.slice(edit.end - offset);
|
|
279
|
+
offset += edit.end - edit.start - edit.replacement.length;
|
|
280
|
+
}
|
|
281
|
+
return { fileContent: updatedTemplate, changed: true, errors: parsed.errors };
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function getComponentMetadataObjects(sourceFile) {
|
|
285
|
+
const results = [];
|
|
286
|
+
utils_typescript.forEachClass(sourceFile, (node) => {
|
|
287
|
+
const decorator = ts
|
|
288
|
+
.getDecorators(node)
|
|
289
|
+
?.find((dec) => ts.isCallExpression(dec.expression) &&
|
|
290
|
+
ts.isIdentifier(dec.expression.expression) &&
|
|
291
|
+
dec.expression.expression.text === 'Component');
|
|
292
|
+
if (!decorator || !ts.isCallExpression(decorator.expression)) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const [arg] = decorator.expression.arguments;
|
|
296
|
+
if (arg && ts.isObjectLiteralExpression(arg)) {
|
|
297
|
+
results.push(arg);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
return results;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Migrates the `host` and `styles` properties of an inline `@Component` decorator — the parts of
|
|
304
|
+
* a component that the HTML-AST template path and `collectInlineTemplateRanges` never see. `host`
|
|
305
|
+
* is matched the same way as a template's `class`/`class.X` attributes; `styles` array entries are
|
|
306
|
+
* plain CSS text, so they're matched with the same word/selector-boundary-safe regex used for
|
|
307
|
+
* `.scss` files.
|
|
308
|
+
*
|
|
309
|
+
* Returns every string-literal range it inspected (`claimedRanges`) — changed or not — so the
|
|
310
|
+
* caller can exclude them from the generic bare-string-literal walk, which would otherwise
|
|
311
|
+
* re-interpret the same text under different (whitespace-token) matching rules.
|
|
312
|
+
*/
|
|
313
|
+
function collectHostAndStylesEdits(sourceFile, tokenMap, stylePatterns) {
|
|
314
|
+
const edits = [];
|
|
315
|
+
const found = [];
|
|
316
|
+
const claimedRanges = [];
|
|
317
|
+
for (const metadata of getComponentMetadataObjects(sourceFile)) {
|
|
318
|
+
for (const prop of metadata.properties) {
|
|
319
|
+
if (!ts.isPropertyAssignment(prop) || (!ts.isIdentifier(prop.name) && !ts.isStringLiteralLike(prop.name))) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
const propName = prop.name.text;
|
|
323
|
+
if (propName === 'host' && ts.isObjectLiteralExpression(prop.initializer)) {
|
|
324
|
+
for (const hostProp of prop.initializer.properties) {
|
|
325
|
+
if (!ts.isPropertyAssignment(hostProp) ||
|
|
326
|
+
(!ts.isIdentifier(hostProp.name) && !ts.isStringLiteralLike(hostProp.name))) {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (ts.isStringLiteralLike(hostProp.name)) {
|
|
330
|
+
claimedRanges.push({
|
|
331
|
+
start: hostProp.name.getStart(sourceFile) + 1,
|
|
332
|
+
end: hostProp.name.getEnd() - 1
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
const hostKey = hostProp.name.text;
|
|
336
|
+
const simpleName = utils_ast.getSimpleAttributeName(hostKey);
|
|
337
|
+
if (simpleName === 'class' && ts.isStringLiteralLike(hostProp.initializer)) {
|
|
338
|
+
const start = hostProp.initializer.getStart(sourceFile) + 1;
|
|
339
|
+
const end = hostProp.initializer.getEnd() - 1;
|
|
340
|
+
claimedRanges.push({ start, end });
|
|
341
|
+
const valueFound = [];
|
|
342
|
+
const newValue = utils_ast.replaceTokenList(sourceFile.text.slice(start, end), exactTokenLookup(tokenMap, valueFound));
|
|
343
|
+
if (newValue !== undefined) {
|
|
344
|
+
edits.push({ start, end, replacement: newValue });
|
|
345
|
+
found.push(...valueFound);
|
|
346
|
+
}
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const bindingToken = utils_ast.getClassBindingToken(simpleName);
|
|
350
|
+
if (bindingToken !== undefined && tokenMap.has(bindingToken)) {
|
|
351
|
+
const replacement = tokenMap.get(bindingToken);
|
|
352
|
+
if (replacement) {
|
|
353
|
+
const rawName = hostProp.name.getText(sourceFile);
|
|
354
|
+
const quote = rawName[0] === "'" || rawName[0] === '"' ? rawName[0] : '';
|
|
355
|
+
const newSimpleName = simpleName.replace(new RegExp(`\\.${escapeRegExp(bindingToken)}$`), `.${replacement}`);
|
|
356
|
+
const newInner = hostKey.startsWith('[') ? `[${newSimpleName}]` : newSimpleName;
|
|
357
|
+
edits.push({
|
|
358
|
+
start: hostProp.name.getStart(sourceFile),
|
|
359
|
+
end: hostProp.name.getEnd(),
|
|
360
|
+
replacement: quote ? `${quote}${newInner}${quote}` : newInner
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
edits.push({
|
|
365
|
+
start: hostProp.getStart(sourceFile),
|
|
366
|
+
end: hostProp.getEnd(),
|
|
367
|
+
replacement: ''
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
found.push({ from: bindingToken, to: replacement ?? '' });
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (propName === 'styles' && ts.isArrayLiteralExpression(prop.initializer)) {
|
|
376
|
+
for (const el of prop.initializer.elements) {
|
|
377
|
+
if (!ts.isStringLiteralLike(el)) {
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
const start = el.getStart(sourceFile) + 1;
|
|
381
|
+
const end = el.getEnd() - 1;
|
|
382
|
+
claimedRanges.push({ start, end });
|
|
383
|
+
const { content, changed, found: styleFound } = replaceKnownIconTokens(sourceFile.text.slice(start, end), stylePatterns, true);
|
|
384
|
+
if (changed) {
|
|
385
|
+
edits.push({ start, end, replacement: content });
|
|
386
|
+
}
|
|
387
|
+
found.push(...styleFound);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return { edits, found, claimedRanges };
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Migrates icon usage in `.ts` files: inline `@Component({ template: '…' })` strings (via
|
|
396
|
+
* `createIconTemplateTransform`, same as external HTML), the `host`/`styles` decorator properties
|
|
397
|
+
* (via `collectHostAndStylesEdits`), and bare string literals outside of any of those (e.g.
|
|
398
|
+
* `return 'pt-icons ' + iconName;`), matched exact-token-only against `tokenMap`.
|
|
399
|
+
*
|
|
400
|
+
* All edits are computed from the same unmodified `sourceFile`/`content` and applied in a single
|
|
401
|
+
* `beginUpdate`/`commitUpdate` pass per file — `Tree`'s `UpdateRecorder` records changes against
|
|
402
|
+
* the file's original offsets regardless of call order, so this only needs to avoid *overlapping*
|
|
403
|
+
* edits, not sequence them.
|
|
404
|
+
*
|
|
405
|
+
* `filePaths` entries must be paths `tree.readText`/`tree.beginUpdate` can resolve directly (i.e.
|
|
406
|
+
* the file's own tree path, not a real filesystem path) — these migrations run against the
|
|
407
|
+
* schematic `Tree`, not the on-disk project. Each file's `ts.SourceFile` is created here, one at a
|
|
408
|
+
* time, rather than by the caller up front, so the whole tree's ASTs are never held in memory at
|
|
409
|
+
* once.
|
|
410
|
+
*
|
|
411
|
+
* `stylePatterns` (built via `buildIconTokenPatterns`) is used for the CSS text inside an inline
|
|
412
|
+
* `styles` array — pass patterns built from a separate map when a bare scope word should be
|
|
413
|
+
* *renamed* there even though it's *dropped* from HTML class lists (see `deprecated-icons/index.ts`).
|
|
414
|
+
*/
|
|
415
|
+
async function migrateIconsInTsFiles(tree, filePaths, context, data, tokenMap, opts, stylePatterns) {
|
|
416
|
+
for (const relativePath of filePaths) {
|
|
417
|
+
const content = tree.readText(relativePath);
|
|
418
|
+
const sourceFile = ts.createSourceFile(relativePath, content, ts.ScriptTarget.Latest, true);
|
|
419
|
+
const templateRanges = utils_typescript.collectInlineTemplateRanges(sourceFile);
|
|
420
|
+
const edits = [];
|
|
421
|
+
const allFound = [];
|
|
422
|
+
const templateTransform = createIconTemplateTransform(data, tokenMap, {
|
|
423
|
+
fix: opts.fix,
|
|
424
|
+
onFound: (_fileName, found) => allFound.push(...found),
|
|
425
|
+
warn: opts.warn
|
|
426
|
+
});
|
|
427
|
+
for (const range of templateRanges) {
|
|
428
|
+
const template = content.slice(range.start, range.end);
|
|
429
|
+
const { fileContent, changed, errors } = await templateTransform(template, relativePath);
|
|
430
|
+
if (errors.length > 0) {
|
|
431
|
+
context.logger.error(errors.map(({ error }) => error.toString()).join('\n'));
|
|
432
|
+
}
|
|
433
|
+
if (changed) {
|
|
434
|
+
edits.push({ start: range.start, end: range.end, replacement: fileContent });
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
const hostAndStyles = collectHostAndStylesEdits(sourceFile, tokenMap, stylePatterns);
|
|
438
|
+
if (opts.fix) {
|
|
439
|
+
edits.push(...hostAndStyles.edits);
|
|
440
|
+
}
|
|
441
|
+
allFound.push(...hostAndStyles.found);
|
|
442
|
+
const claimedRanges = [...templateRanges, ...hostAndStyles.claimedRanges];
|
|
443
|
+
utils_typescript.forEachStringLiteral(sourceFile, (node) => {
|
|
444
|
+
const start = node.getStart(sourceFile) + 1;
|
|
445
|
+
const end = node.getEnd() - 1;
|
|
446
|
+
const isClaimed = claimedRanges.some((range) => start >= range.start && end <= range.end);
|
|
447
|
+
if (isClaimed) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
const resolved = resolveBareStringLiteral(content.slice(start, end), tokenMap, data);
|
|
451
|
+
if (resolved !== undefined) {
|
|
452
|
+
if (opts.fix) {
|
|
453
|
+
edits.push({ start, end, replacement: resolved.result });
|
|
454
|
+
}
|
|
455
|
+
allFound.push(...resolved.found);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
if (!opts.fix) {
|
|
459
|
+
if (allFound.length > 0) {
|
|
460
|
+
opts.onFound(relativePath, allFound);
|
|
461
|
+
}
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (edits.length === 0) {
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
const update = tree.beginUpdate(relativePath);
|
|
468
|
+
for (const edit of edits) {
|
|
469
|
+
update.remove(edit.start, edit.end - edit.start);
|
|
470
|
+
update.insertLeft(edit.start, edit.replacement);
|
|
471
|
+
}
|
|
472
|
+
tree.commitUpdate(update);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Precompiles the word/selector-boundary-safe regex for every `tokenMap` entry once, so a run over
|
|
477
|
+
* many style/markdown files (and inline `styles` arrays) can reuse the same `IconTokenPattern[]`
|
|
478
|
+
* instead of rebuilding every regex — there can be 1000+ tokens — on every single file.
|
|
479
|
+
*/
|
|
480
|
+
function buildIconTokenPatterns(tokenMap,
|
|
481
|
+
/** The bare, unprefixed scope word (`data.scope.from`), if present in `tokenMap`. Unlike every
|
|
482
|
+
* other token — which is a specific, hyphenated `scope-suffix` string — this one is short and
|
|
483
|
+
* generic enough (e.g. `mc`) to collide with unrelated identifiers (`$mc: red;`,
|
|
484
|
+
* `@import 'mc'`, `font-family: mc;`). Matched only when immediately preceded by `.`, so it
|
|
485
|
+
* still catches a real class selector (`.mc {}`) without touching those. */
|
|
486
|
+
bareScopeWord) {
|
|
487
|
+
return Array.from(tokenMap, ([token, replacement]) => ({
|
|
488
|
+
token,
|
|
489
|
+
replacement,
|
|
490
|
+
pattern: token === bareScopeWord
|
|
491
|
+
? new RegExp(`(?<=\\.)${escapeRegExp(token)}(?![\\w-])`, 'g')
|
|
492
|
+
: new RegExp(`(?<![\\w-])${escapeRegExp(token)}(?![\\w-])`, 'g')
|
|
493
|
+
}));
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Word/selector-boundary-safe regex replace over raw style-file text, using patterns precompiled
|
|
497
|
+
* by `buildIconTokenPatterns` — e.g. `.pt-icons {}` and `@extend .pt-icons;` match (preceded by
|
|
498
|
+
* `.`, a non-word/non-hyphen char), but `.my-widget-pt-icons-preview` and `mc-word-wrap_16-suffix`
|
|
499
|
+
* do not. When `fix` is false, only collects `found` without mutating.
|
|
500
|
+
*
|
|
501
|
+
* Each pattern's `lastIndex` is reset before use: a global regex's `String.replace` resets it
|
|
502
|
+
* internally, but `RegExp.test` does not — reusing the same pattern object (the whole point of
|
|
503
|
+
* precompiling once) across multiple, unrelated `content` strings would otherwise carry a match
|
|
504
|
+
* position from one file into the next and silently miss real matches in the next file.
|
|
505
|
+
*/
|
|
506
|
+
function replaceKnownIconTokens(content, patterns, fix) {
|
|
507
|
+
const found = [];
|
|
508
|
+
let result = content;
|
|
509
|
+
for (const { token, replacement, pattern } of patterns) {
|
|
510
|
+
pattern.lastIndex = 0;
|
|
511
|
+
if (fix) {
|
|
512
|
+
const next = result.replace(pattern, replacement ?? '');
|
|
513
|
+
if (next !== result) {
|
|
514
|
+
found.push({ from: token, to: replacement ?? '' });
|
|
515
|
+
result = next;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
else if (pattern.test(content)) {
|
|
519
|
+
found.push({ from: token, to: replacement ?? '' });
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return { content: fix ? result : content, changed: fix && found.length > 0, found };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
exports.IconAttributeCollector = IconAttributeCollector;
|
|
526
|
+
exports.buildIconTokenMap = buildIconTokenMap;
|
|
527
|
+
exports.buildIconTokenPatterns = buildIconTokenPatterns;
|
|
528
|
+
exports.createIconTemplateTransform = createIconTemplateTransform;
|
|
529
|
+
exports.migrateIconsInTsFiles = migrateIconsInTsFiles;
|
|
530
|
+
exports.replaceKnownIconTokens = replaceKnownIconTokens;
|
|
@@ -12,6 +12,48 @@ function forEachClass(sourceFile, callback) {
|
|
|
12
12
|
node.forEachChild(walk);
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
|
+
/** Walks every string literal (`'...'`/`"..."` or a template literal with no substitutions) in a source file. */
|
|
16
|
+
function forEachStringLiteral(sourceFile, callback) {
|
|
17
|
+
sourceFile.forEachChild(function walk(node) {
|
|
18
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
19
|
+
callback(node);
|
|
20
|
+
}
|
|
21
|
+
node.forEachChild(walk);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Interior `[start, end)` ranges of inline `@Component({ template: '…' })` string literals.
|
|
26
|
+
*
|
|
27
|
+
* Discovered through the AST rather than by scanning for `template:`, so a `template` property of
|
|
28
|
+
* some other object, or one mentioned in a comment or a string, is never mistaken for a template.
|
|
29
|
+
* A template literal with a `${…}` substitution is skipped — its text is not a template on its own.
|
|
30
|
+
*/
|
|
31
|
+
function collectInlineTemplateRanges(sourceFile) {
|
|
32
|
+
const ranges = [];
|
|
33
|
+
forEachClass(sourceFile, (node) => {
|
|
34
|
+
const decorator = ts
|
|
35
|
+
.getDecorators(node)
|
|
36
|
+
?.find((dec) => ts.isCallExpression(dec.expression) &&
|
|
37
|
+
ts.isIdentifier(dec.expression.expression) &&
|
|
38
|
+
dec.expression.expression.text === 'Component');
|
|
39
|
+
if (!decorator || !ts.isCallExpression(decorator.expression))
|
|
40
|
+
return;
|
|
41
|
+
const [arg] = decorator.expression.arguments;
|
|
42
|
+
if (!arg || !ts.isObjectLiteralExpression(arg))
|
|
43
|
+
return;
|
|
44
|
+
for (const prop of arg.properties) {
|
|
45
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
46
|
+
(ts.isIdentifier(prop.name) || ts.isStringLiteralLike(prop.name)) &&
|
|
47
|
+
prop.name.text === 'template' &&
|
|
48
|
+
ts.isStringLiteralLike(prop.initializer) &&
|
|
49
|
+
prop.initializer.text) {
|
|
50
|
+
// +1 / -1 to exclude the opening/closing quote characters.
|
|
51
|
+
ranges.push({ start: prop.initializer.getStart(sourceFile) + 1, end: prop.initializer.getEnd() - 1 });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return ranges;
|
|
56
|
+
}
|
|
15
57
|
function canMigrateFile(sourceFile, program) {
|
|
16
58
|
// We shouldn't migrate .d.ts files, files from an external library or type checking files.
|
|
17
59
|
return !(sourceFile.fileName.endsWith('.ngtypecheck.ts') ||
|
|
@@ -165,7 +207,9 @@ exports.AnalyzedFile = AnalyzedFile;
|
|
|
165
207
|
exports.ElementCollector = ElementCollector;
|
|
166
208
|
exports.LEADING_TRIVIA_CHARS = LEADING_TRIVIA_CHARS;
|
|
167
209
|
exports.canMigrateFile = canMigrateFile;
|
|
210
|
+
exports.collectInlineTemplateRanges = collectInlineTemplateRanges;
|
|
168
211
|
exports.forEachClass = forEachClass;
|
|
212
|
+
exports.forEachStringLiteral = forEachStringLiteral;
|
|
169
213
|
exports.getClassWithUpdatedDecorator = getClassWithUpdatedDecorator;
|
|
170
214
|
exports.parseTemplate = parseTemplate;
|
|
171
215
|
exports.updateDecoratorProperty = updateDecoratorProperty;
|
package/scrollbar/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as overlayscrollbars from 'overlayscrollbars';
|
|
2
|
-
import {
|
|
2
|
+
import { PartialOptions, EventListeners, InitializationTarget, EventListenerArgs, OverlayScrollbars } from 'overlayscrollbars';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { InjectionToken, Provider, AfterViewInit, OnDestroy, ElementRef, EventEmitter } from '@angular/core';
|
|
5
5
|
import { KbqOverflowShadowSource } from '@koobiq/components/core';
|
|
@@ -35,6 +35,8 @@ declare class KbqSearchExpandable implements ControlValueAccessor, AfterViewInit
|
|
|
35
35
|
/** Current value in input. */
|
|
36
36
|
value: BehaviorSubject<string>;
|
|
37
37
|
protected lastFocusOrigin: 'touch' | 'mouse' | 'keyboard' | 'program' | null;
|
|
38
|
+
/** Suppress the automatic input focus once — set when the component auto-opens from a model value. */
|
|
39
|
+
private suppressInputFocus;
|
|
38
40
|
/** state of component. */
|
|
39
41
|
isOpened: boolean;
|
|
40
42
|
/** Emit event by enter or not. Default is false */
|
|
@@ -77,6 +79,7 @@ declare class KbqSearchExpandable implements ControlValueAccessor, AfterViewInit
|
|
|
77
79
|
/** Implemented as part of ControlValueAccessor. */
|
|
78
80
|
setDisabledState(isDisabled: boolean): void;
|
|
79
81
|
toggle(): void;
|
|
82
|
+
private setOpened;
|
|
80
83
|
private updateLocaleParams;
|
|
81
84
|
private initDefaultParams;
|
|
82
85
|
private emitValue;
|