@mk-kit/ui 0.38.0 → 0.39.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/README.md +6 -1
- package/package.json +1 -1
- package/schematics/collection.json +5 -0
- package/schematics/migrate-primeng/index.js +76 -0
- package/schematics/migrate-primeng/mapping.js +289 -0
- package/schematics/migrate-primeng/schema.json +25 -0
- package/schematics/migrate-primeng/transform.js +325 -0
package/README.md
CHANGED
|
@@ -23,7 +23,12 @@ ng add @mk-kit/ui
|
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
`ng add` installs the package and wires the theme stylesheet into
|
|
26
|
-
`angular.json`.
|
|
26
|
+
`angular.json`.
|
|
27
|
+
|
|
28
|
+
**Coming from PrimeNG?** `ng g @mk-kit/ui:migrate-primeng --dry-run` shows
|
|
29
|
+
what the codemod would rewrite (imports, class names, 1:1 selectors, a few
|
|
30
|
+
input names) and what needs a manual touch; without `--dry-run` it applies
|
|
31
|
+
the changes and writes `primeng-migration.md`. Details: <https://mk-kit.dev/migration>. By hand instead:
|
|
27
32
|
|
|
28
33
|
```bash
|
|
29
34
|
npm install @mk-kit/ui
|
package/package.json
CHANGED
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
"description": "Adds @mk-kit/ui to an application: wires the theme stylesheet into angular.json and optionally scaffolds a provideMkI18n override block.",
|
|
6
6
|
"factory": "./ng-add/index#ngAdd",
|
|
7
7
|
"schema": "./ng-add/schema.json"
|
|
8
|
+
},
|
|
9
|
+
"migrate-primeng": {
|
|
10
|
+
"description": "Migrates a PrimeNG app to @mk-kit/ui: rewrites imports, class names and 1:1 selectors, annotates what needs a manual touch, cleans angular.json / package.json and writes a report.",
|
|
11
|
+
"factory": "./migrate-primeng/index#migratePrimeng",
|
|
12
|
+
"schema": "./migrate-primeng/schema.json"
|
|
8
13
|
}
|
|
9
14
|
}
|
|
10
15
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.migratePrimeng = migratePrimeng;
|
|
4
|
+
const mapping_1 = require("./mapping");
|
|
5
|
+
const transform_1 = require("./transform");
|
|
6
|
+
const SKIP_DIRS = new Set(['node_modules', 'dist', '.angular', '.git']);
|
|
7
|
+
function migratePrimeng(options) {
|
|
8
|
+
return (tree, context) => {
|
|
9
|
+
const root = normalize(options.path ?? 'src');
|
|
10
|
+
const dryRun = !!options.dryRun;
|
|
11
|
+
const reportPath = options.report ?? 'primeng-migration.md';
|
|
12
|
+
const files = [];
|
|
13
|
+
let scanned = 0;
|
|
14
|
+
tree.getDir(root).visit((path) => {
|
|
15
|
+
if (path.split('/').some((seg) => SKIP_DIRS.has(seg)))
|
|
16
|
+
return;
|
|
17
|
+
const isTs = path.endsWith('.ts') && !path.endsWith('.d.ts');
|
|
18
|
+
const isHtml = path.endsWith('.html');
|
|
19
|
+
if (!isTs && !isHtml)
|
|
20
|
+
return;
|
|
21
|
+
scanned++;
|
|
22
|
+
const source = tree.read(path)?.toString('utf8');
|
|
23
|
+
if (source === undefined || !/primeng|<p-[a-zA-Z]|\bp[A-Z][a-zA-Z]+\b/.test(source))
|
|
24
|
+
return;
|
|
25
|
+
const result = isTs ? (0, transform_1.transformTypeScript)(source) : (0, transform_1.transformTemplate)(source);
|
|
26
|
+
if (!result.findings.length && !result.changed)
|
|
27
|
+
return;
|
|
28
|
+
files.push({ path, findings: result.findings, changed: result.changed });
|
|
29
|
+
if (result.changed && !dryRun)
|
|
30
|
+
tree.overwrite(path, result.text);
|
|
31
|
+
});
|
|
32
|
+
const unmapped = files.some((f) => f.findings.some((x) => x.kind === 'unmapped'));
|
|
33
|
+
for (const [path, fn] of [
|
|
34
|
+
['/angular.json', transform_1.transformAngularJson],
|
|
35
|
+
['/package.json', (src) => (0, transform_1.transformPackageJson)(src, unmapped ? [] : mapping_1.PRIMENG_PACKAGES)],
|
|
36
|
+
]) {
|
|
37
|
+
if (!tree.exists(path))
|
|
38
|
+
continue;
|
|
39
|
+
scanned++;
|
|
40
|
+
const result = fn(tree.read(path).toString('utf8'));
|
|
41
|
+
if (!result.findings.length)
|
|
42
|
+
continue;
|
|
43
|
+
files.push({ path, findings: result.findings, changed: result.changed });
|
|
44
|
+
if (result.changed && !dryRun)
|
|
45
|
+
tree.overwrite(path, result.text);
|
|
46
|
+
}
|
|
47
|
+
if (unmapped) {
|
|
48
|
+
files.push({
|
|
49
|
+
path: '/package.json',
|
|
50
|
+
changed: false,
|
|
51
|
+
findings: [{ rule: 'package:keep', message: 'primeng kept in package.json: some components have no mk-kit equivalent yet (see "Not available")', kind: 'manual', count: 1 }],
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const report = (0, transform_1.renderReport)(files, { dryRun, scanned });
|
|
55
|
+
if (!dryRun) {
|
|
56
|
+
if (tree.exists(reportPath))
|
|
57
|
+
tree.overwrite(reportPath, report);
|
|
58
|
+
else
|
|
59
|
+
tree.create(reportPath, report);
|
|
60
|
+
}
|
|
61
|
+
const counts = { rewrite: 0, manual: 0, unmapped: 0 };
|
|
62
|
+
for (const f of files)
|
|
63
|
+
for (const x of f.findings)
|
|
64
|
+
counts[x.kind] += x.count;
|
|
65
|
+
context.logger.info(`migrate-primeng: ${dryRun ? 'dry run — ' : ''}${files.filter((f) => f.changed).length} file(s) ${dryRun ? 'would change' : 'changed'}; ` +
|
|
66
|
+
`${counts.rewrite} rewrites, ${counts.manual} manual steps, ${counts.unmapped} without an equivalent.` +
|
|
67
|
+
(dryRun ? '' : ` Report: ${reportPath}`));
|
|
68
|
+
if (dryRun)
|
|
69
|
+
context.logger.info('\n' + report);
|
|
70
|
+
return tree;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function normalize(p) {
|
|
74
|
+
const trimmed = p.replace(/\/+$/, '');
|
|
75
|
+
return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
|
|
76
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* PrimeNG → @mk-kit/ui mapping used by the `migrate-primeng` schematic.
|
|
4
|
+
*
|
|
5
|
+
* Two tables:
|
|
6
|
+
* - `MODULES`: what each `primeng/<x>` import path exports and which mk-kit
|
|
7
|
+
* class replaces each symbol (`null` = no drop-in equivalent → reported).
|
|
8
|
+
* - `SELECTORS`: template-level rewrites. `to` set + `exact` true means a
|
|
9
|
+
* mechanical rename is safe; otherwise the original markup is left alone
|
|
10
|
+
* and a `<!-- mk-kit: … -->` note is inserted once per file.
|
|
11
|
+
*
|
|
12
|
+
* Everything here is checked against the library's real exports/selectors
|
|
13
|
+
* (see projects/docs/public/api.json); keep it that way when editing.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.PRIMENG_PACKAGES = exports.DOCS_BASE = exports.SELECTORS = exports.MODULES = void 0;
|
|
17
|
+
const D = 'https://mk-kit.dev';
|
|
18
|
+
exports.MODULES = [
|
|
19
|
+
{ path: 'button', symbols: { ButtonModule: 'MkButton', Button: 'MkButton', ButtonDirective: 'MkButton' }, docs: '/components/buttons' },
|
|
20
|
+
{ path: 'splitbutton', symbols: { SplitButtonModule: 'MkSplitButton', SplitButton: 'MkSplitButton' }, note: 'mk-split-button takes an mk-menu via [menu] instead of [model].', docs: '/components/buttons' },
|
|
21
|
+
{ path: 'inputtext', symbols: { InputTextModule: 'MkInput', InputText: 'MkInput' }, docs: '/components/text-inputs' },
|
|
22
|
+
{ path: 'textarea', symbols: { TextareaModule: 'MkInput', Textarea: 'MkInput' }, docs: '/components/text-inputs' },
|
|
23
|
+
{ path: 'inputtextarea', symbols: { InputTextareaModule: 'MkInput', InputTextarea: 'MkInput' }, docs: '/components/text-inputs' },
|
|
24
|
+
{ path: 'inputnumber', symbols: { InputNumberModule: 'MkNumberInput', InputNumber: 'MkNumberInput' }, docs: '/components/text-inputs' },
|
|
25
|
+
{ path: 'password', symbols: { PasswordModule: 'MkPasswordInput', Password: 'MkPasswordInput' }, docs: '/components/text-inputs' },
|
|
26
|
+
{ path: 'inputotp', symbols: { InputOtpModule: 'MkOtp', InputOtp: 'MkOtp' }, docs: '/components/text-inputs' },
|
|
27
|
+
{ path: 'checkbox', symbols: { CheckboxModule: 'MkCheckbox', Checkbox: 'MkCheckbox' }, docs: '/components/toggles' },
|
|
28
|
+
{ path: 'radiobutton', symbols: { RadioButtonModule: 'MkRadioGroup', RadioButton: 'MkRadioGroup' }, note: 'mk-radio-group wraps mk-radio children; the group owns the value.', docs: '/components/toggles' },
|
|
29
|
+
{ path: 'toggleswitch', symbols: { ToggleSwitchModule: 'MkSwitch', ToggleSwitch: 'MkSwitch' }, docs: '/components/toggles' },
|
|
30
|
+
{ path: 'inputswitch', symbols: { InputSwitchModule: 'MkSwitch', InputSwitch: 'MkSwitch' }, docs: '/components/toggles' },
|
|
31
|
+
{ path: 'select', symbols: { SelectModule: 'MkSelect', Select: 'MkSelect' }, note: 'mk-select takes [options] as {label, value}[] — no optionLabel/optionValue.', docs: '/components/forms' },
|
|
32
|
+
{ path: 'dropdown', symbols: { DropdownModule: 'MkSelect', Dropdown: 'MkSelect' }, note: 'mk-select takes [options] as {label, value}[] — no optionLabel/optionValue.', docs: '/components/forms' },
|
|
33
|
+
{ path: 'multiselect', symbols: { MultiSelectModule: 'MkMultiSelect', MultiSelect: 'MkMultiSelect' }, note: 'mk-multi-select takes [options] as {label, value}[].', docs: '/components/selection' },
|
|
34
|
+
{ path: 'listbox', symbols: { ListboxModule: 'MkListbox', Listbox: 'MkListbox' }, docs: '/components/selection' },
|
|
35
|
+
{ path: 'autocomplete', symbols: { AutoCompleteModule: 'MkAutocomplete', AutoComplete: 'MkAutocomplete' }, note: 'Pass [options] (sync or async) instead of (completeMethod) + [suggestions].', docs: '/components/selection' },
|
|
36
|
+
{ path: 'cascadeselect', symbols: { CascadeSelectModule: 'MkCascader', CascadeSelect: 'MkCascader' }, docs: '/components/selection' },
|
|
37
|
+
{ path: 'treeselect', symbols: { TreeSelectModule: 'MkTreeSelect', TreeSelect: 'MkTreeSelect' }, docs: '/components/selection' },
|
|
38
|
+
{ path: 'selectbutton', symbols: { SelectButtonModule: 'MkButtonToggleGroup', SelectButton: 'MkButtonToggleGroup' }, note: 'mk-button-toggle-group uses mk-button-toggle children instead of [options].', docs: '/components/selection' },
|
|
39
|
+
{ path: 'togglebutton', symbols: { ToggleButtonModule: 'MkButtonToggleGroup', ToggleButton: 'MkButtonToggleGroup' }, docs: '/components/selection' },
|
|
40
|
+
{ path: 'datepicker', symbols: { DatePickerModule: 'MkDatePicker', DatePicker: 'MkDatePicker' }, docs: '/components/date-time' },
|
|
41
|
+
{ path: 'calendar', symbols: { CalendarModule: 'MkDatePicker', Calendar: 'MkDatePicker' }, docs: '/components/date-time' },
|
|
42
|
+
{ path: 'rating', symbols: { RatingModule: 'MkRating', Rating: 'MkRating' }, docs: '/components/sliders' },
|
|
43
|
+
{ path: 'slider', symbols: { SliderModule: 'MkSlider', Slider: 'MkSlider' }, note: 'Range mode → mk-range-slider.', docs: '/components/sliders' },
|
|
44
|
+
{ path: 'colorpicker', symbols: { ColorPickerModule: 'MkColorPicker', ColorPicker: 'MkColorPicker' }, docs: '/components/sliders' },
|
|
45
|
+
{ path: 'fileupload', symbols: { FileUploadModule: 'MkFileUpload', FileUpload: 'MkFileUpload' }, note: 'Replace [url] + (onUpload) with [uploadFn].', docs: '/components/forms' },
|
|
46
|
+
{ path: 'editor', symbols: { EditorModule: 'MkRichText', Editor: 'MkRichText' }, docs: '/components/rich-text' },
|
|
47
|
+
{ path: 'knob', symbols: { KnobModule: 'MkProgressRing', Knob: 'MkProgressRing' }, note: 'mk-progress-ring is display-only; for an editable dial use mk-slider.', docs: '/components/proportion-charts' },
|
|
48
|
+
{ path: 'table', symbols: { TableModule: 'MkTable', Table: 'MkTable' }, note: 'mk-table renders from [columns] + [data]; templates via mkTableCell. Server-side paging/sorting → MkTableDataSource.', docs: '/components/table' },
|
|
49
|
+
{ path: 'paginator', symbols: { PaginatorModule: 'MkPagination', Paginator: 'MkPagination' }, note: 'mk-pagination is 1-based with [total] + [pageSize]; no [first]/[rows].', docs: '/components/navigation' },
|
|
50
|
+
{ path: 'tree', symbols: { TreeModule: 'MkTree', Tree: 'MkTree' }, note: 'Node shape is {label, children, key}; use [nodes].', docs: '/components/tree' },
|
|
51
|
+
{ path: 'treetable', symbols: { TreeTableModule: 'MkTable', TreeTable: 'MkTable' }, note: 'mk-table with [childrenKey] renders tree rows.', docs: '/components/table' },
|
|
52
|
+
{ path: 'dataview', symbols: { DataViewModule: null, DataView: null }, note: 'No data-view: compose mk-list / mk-grid with @for.', docs: '/components/cards-lists' },
|
|
53
|
+
{ path: 'orderlist', symbols: { OrderListModule: 'MkSortableList', OrderList: 'MkSortableList' }, docs: '/components/drag-drop' },
|
|
54
|
+
{ path: 'picklist', symbols: { PickListModule: 'MkTransferList', PickList: 'MkTransferList' }, docs: '/components/selection' },
|
|
55
|
+
{ path: 'scroller', symbols: { ScrollerModule: 'MkVirtualScroll', Scroller: 'MkVirtualScroll' }, docs: '/components/data' },
|
|
56
|
+
{ path: 'virtualscroller', symbols: { VirtualScrollerModule: 'MkVirtualScroll', VirtualScroller: 'MkVirtualScroll' }, docs: '/components/data' },
|
|
57
|
+
{ path: 'timeline', symbols: { TimelineModule: 'MkTimeline', Timeline: 'MkTimeline' }, note: 'Use mk-timeline-item children instead of [value] + templates.', docs: '/components/empty-timeline' },
|
|
58
|
+
{ path: 'organizationchart', symbols: { OrganizationChartModule: null, OrganizationChart: null }, note: 'No org chart yet.', docs: '/components/tree' },
|
|
59
|
+
{ path: 'chart', symbols: { ChartModule: null, UIChart: null }, note: 'p-chart wraps Chart.js. mk-kit ships SVG charts: mk-line-chart, mk-bar-chart, mk-donut-chart, mk-gauge, … — map by [type].', docs: '/components/charts' },
|
|
60
|
+
{ path: 'dialog', symbols: { DialogModule: 'MkDialog', Dialog: 'MkDialog' }, note: 'mk-dialog is opened through MkDialogService.open(Component) — no [(visible)] toggle. Move the dialog body into its own component.', docs: '/components/dialogs' },
|
|
61
|
+
{ path: 'dynamicdialog', symbols: { DynamicDialogModule: null, DialogService: 'MkDialogService', DynamicDialogRef: 'MkOverlayRef', DynamicDialogConfig: null }, note: 'MkDialogService.open(Cmp, { data }); inject MK_OVERLAY_DATA in the component; await ref.afterClosed.', docs: '/components/dialogs' },
|
|
62
|
+
{ path: 'confirmdialog', symbols: { ConfirmDialogModule: null, ConfirmDialog: null }, note: 'Use MkDialogService.confirm({ title, message, tone }) — no <p-confirmDialog> host element needed.', docs: '/components/dialogs' },
|
|
63
|
+
{ path: 'confirmpopup', symbols: { ConfirmPopupModule: 'MkPopconfirm', ConfirmPopup: 'MkPopconfirm' }, note: 'Use [mkPopconfirmFor] on the trigger.', docs: '/components/popovers' },
|
|
64
|
+
{ path: 'toast', symbols: { ToastModule: null, Toast: null }, note: 'No <p-toast> host: inject MkToastService and call success/info/warning/danger(message, { title }).', docs: '/components/snackbar' },
|
|
65
|
+
{ path: 'message', symbols: { MessageModule: 'MkAlert', Message: 'MkAlert' }, docs: '/components/feedback' },
|
|
66
|
+
{ path: 'messages', symbols: { MessagesModule: 'MkAlert', Messages: 'MkAlert' }, note: 'One mk-alert per message.', docs: '/components/feedback' },
|
|
67
|
+
{ path: 'tooltip', symbols: { TooltipModule: 'MkTooltip', Tooltip: 'MkTooltip' }, docs: '/components/popovers' },
|
|
68
|
+
{ path: 'popover', symbols: { PopoverModule: 'MkPopover', Popover: 'MkPopover' }, note: 'Use [mkPopoverTriggerFor] on the trigger instead of #op.toggle($event).', docs: '/components/popovers' },
|
|
69
|
+
{ path: 'overlaypanel', symbols: { OverlayPanelModule: 'MkPopover', OverlayPanel: 'MkPopover' }, note: 'Use [mkPopoverTriggerFor] on the trigger instead of #op.toggle($event).', docs: '/components/popovers' },
|
|
70
|
+
{ path: 'drawer', symbols: { DrawerModule: 'MkDrawer', Drawer: 'MkDrawer' }, docs: '/components/drawer' },
|
|
71
|
+
{ path: 'sidebar', symbols: { SidebarModule: 'MkDrawer', Sidebar: 'MkDrawer' }, docs: '/components/drawer' },
|
|
72
|
+
{ path: 'blockui', symbols: { BlockUIModule: 'MkBlockUi', BlockUI: 'MkBlockUi' }, note: 'Use the [mkBlockUi] directive on the region.', docs: '/components/feedback' },
|
|
73
|
+
{ path: 'progressbar', symbols: { ProgressBarModule: 'MkProgressBar', ProgressBar: 'MkProgressBar' }, docs: '/components/loading' },
|
|
74
|
+
{ path: 'progressspinner', symbols: { ProgressSpinnerModule: 'MkSpinner', ProgressSpinner: 'MkSpinner' }, docs: '/components/loading' },
|
|
75
|
+
{ path: 'skeleton', symbols: { SkeletonModule: 'MkSkeleton', Skeleton: 'MkSkeleton' }, docs: '/components/loading' },
|
|
76
|
+
{ path: 'badge', symbols: { BadgeModule: 'MkBadge', Badge: 'MkBadge', BadgeDirective: 'MkBadge' }, note: 'pBadge attribute → <mk-badge> element next to the target.', docs: '/components/badges-avatars' },
|
|
77
|
+
{ path: 'tag', symbols: { TagModule: 'MkTag', Tag: 'MkTag' }, docs: '/components/badges-avatars' },
|
|
78
|
+
{ path: 'chip', symbols: { ChipModule: 'MkChip', Chip: 'MkChip' }, docs: '/components/badges-avatars' },
|
|
79
|
+
{ path: 'avatar', symbols: { AvatarModule: 'MkAvatar', Avatar: 'MkAvatar' }, docs: '/components/badges-avatars' },
|
|
80
|
+
{ path: 'avatargroup', symbols: { AvatarGroupModule: 'MkAvatarGroup', AvatarGroup: 'MkAvatarGroup' }, docs: '/components/badges-avatars' },
|
|
81
|
+
{ path: 'card', symbols: { CardModule: 'MkCard', Card: 'MkCard' }, note: 'Header/footer via mk-card-header / mk-card-footer.', docs: '/components/cards-lists' },
|
|
82
|
+
{ path: 'panel', symbols: { PanelModule: 'MkCard', Panel: 'MkCard' }, note: 'Collapsible panel → mk-accordion-item.', docs: '/components/cards-lists' },
|
|
83
|
+
{ path: 'fieldset', symbols: { FieldsetModule: 'MkCard', Fieldset: 'MkCard' }, docs: '/components/cards-lists' },
|
|
84
|
+
{ path: 'divider', symbols: { DividerModule: 'MkDivider', Divider: 'MkDivider' }, docs: '/components/cards-lists' },
|
|
85
|
+
{ path: 'toolbar', symbols: { ToolbarModule: 'MkToolbar', Toolbar: 'MkToolbar' }, docs: '/components/structure' },
|
|
86
|
+
{ path: 'splitter', symbols: { SplitterModule: 'MkSplitter', Splitter: 'MkSplitter' }, docs: '/components/structure' },
|
|
87
|
+
{ path: 'accordion', symbols: { AccordionModule: 'MkAccordion', Accordion: 'MkAccordion', AccordionPanel: 'MkAccordionItem', AccordionHeader: 'MkAccordionHeader', AccordionContent: null, AccordionTab: 'MkAccordionItem' }, docs: '/components/navigation' },
|
|
88
|
+
{ path: 'tabs', symbols: { TabsModule: 'MkTabs', Tabs: 'MkTabs', TabList: null, Tab: 'MkTab', TabPanels: null, TabPanel: null }, note: 'mk-tabs uses <mk-tab label="…"> children: merge each p-tab + p-tabpanel pair.', docs: '/components/navigation' },
|
|
89
|
+
{ path: 'tabview', symbols: { TabViewModule: 'MkTabs', TabView: 'MkTabs', TabPanel: 'MkTab' }, docs: '/components/navigation' },
|
|
90
|
+
{ path: 'stepper', symbols: { StepperModule: 'MkStepper', Stepper: 'MkStepper', StepList: null, Step: 'MkStep', StepPanels: null, StepPanel: null }, docs: '/components/stepper' },
|
|
91
|
+
{ path: 'steps', symbols: { StepsModule: 'MkStepper', Steps: 'MkStepper' }, note: 'Use mk-step children instead of [model].', docs: '/components/stepper' },
|
|
92
|
+
{ path: 'menu', symbols: { MenuModule: 'MkMenu', Menu: 'MkMenu' }, note: 'Use mk-menu-item children instead of [model]; open with [mkMenuTriggerFor].', docs: '/components/navigation' },
|
|
93
|
+
{ path: 'menubar', symbols: { MenubarModule: 'MkNavList', Menubar: 'MkNavList' }, note: 'Horizontal nav → mk-nav-list / mk-toolbar with mkButton links.', docs: '/components/command-nav' },
|
|
94
|
+
{ path: 'tieredmenu', symbols: { TieredMenuModule: 'MkMenu', TieredMenu: 'MkMenu' }, note: 'Nested items via [mkSubmenuFor].', docs: '/components/navigation' },
|
|
95
|
+
{ path: 'contextmenu', symbols: { ContextMenuModule: 'MkContextMenuTrigger', ContextMenu: 'MkContextMenuTrigger' }, note: 'Use [mkContextMenuTriggerFor]="menu" on the element.', docs: '/components/context-menu' },
|
|
96
|
+
{ path: 'megamenu', symbols: { MegaMenuModule: null, MegaMenu: null }, note: 'No mega menu; compose mk-popover + mk-nav-list.', docs: '/components/command-nav' },
|
|
97
|
+
{ path: 'panelmenu', symbols: { PanelMenuModule: 'MkNavList', PanelMenu: 'MkNavList' }, note: 'mk-nav-list with mk-nav-group children.', docs: '/components/command-nav' },
|
|
98
|
+
{ path: 'breadcrumb', symbols: { BreadcrumbModule: 'MkBreadcrumb', Breadcrumb: 'MkBreadcrumb' }, note: 'Use mk-breadcrumb-item children instead of [model].', docs: '/components/navigation' },
|
|
99
|
+
{ path: 'dock', symbols: { DockModule: null, Dock: null }, note: 'No dock.', docs: '/components/navigation' },
|
|
100
|
+
{ path: 'carousel', symbols: { CarouselModule: 'MkCarousel', Carousel: 'MkCarousel' }, note: 'Use mkCarouselSlide templates instead of [value].', docs: '/components/data' },
|
|
101
|
+
{ path: 'galleria', symbols: { GalleriaModule: 'MkImageGallery', Galleria: 'MkImageGallery' }, docs: '/components/images' },
|
|
102
|
+
{ path: 'image', symbols: { ImageModule: 'MkImage', Image: 'MkImage' }, note: 'Preview → MkLightboxService.', docs: '/components/images' },
|
|
103
|
+
{ path: 'scrollpanel', symbols: { ScrollPanelModule: 'MkScrollArea', ScrollPanel: 'MkScrollArea' }, docs: '/components/structure' },
|
|
104
|
+
{ path: 'scrolltop', symbols: { ScrollTopModule: 'MkBackToTop', ScrollTop: 'MkBackToTop' }, docs: '/components/navigation' },
|
|
105
|
+
{ path: 'speeddial', symbols: { SpeedDialModule: 'MkFab', SpeedDial: 'MkFab' }, note: 'mk-fab with mkFabAction children.', docs: '/components/navigation' },
|
|
106
|
+
{ path: 'inplace', symbols: { InplaceModule: 'MkInlineEdit', Inplace: 'MkInlineEdit' }, docs: '/components/table' },
|
|
107
|
+
{ path: 'metergroup', symbols: { MeterGroupModule: null, MeterGroup: null }, note: 'No meter group yet; mk-progress-bar per segment.', docs: '/components/loading' },
|
|
108
|
+
{ path: 'terminal', symbols: { TerminalModule: null, Terminal: null }, note: 'No terminal.', docs: '/components/data' },
|
|
109
|
+
{ path: 'iconfield', symbols: { IconFieldModule: 'MkInputGroup', IconField: 'MkInputGroup', InputIcon: 'MkIcon', InputIconModule: 'MkIcon' }, note: 'mk-input-group with a prefix slot.', docs: '/components/text-inputs' },
|
|
110
|
+
{ path: 'inputgroup', symbols: { InputGroupModule: 'MkInputGroup', InputGroup: 'MkInputGroup' }, docs: '/components/text-inputs' },
|
|
111
|
+
{ path: 'inputgroupaddon', symbols: { InputGroupAddonModule: null, InputGroupAddon: null }, note: 'Addons are content slots of mk-input-group.', docs: '/components/text-inputs' },
|
|
112
|
+
{ path: 'floatlabel', symbols: { FloatLabelModule: 'MkFormField', FloatLabel: 'MkFormField' }, note: 'mk-form-field labelPosition="float".', docs: '/components/forms' },
|
|
113
|
+
{ path: 'iftalabel', symbols: { IftaLabelModule: 'MkFormField', IftaLabel: 'MkFormField' }, docs: '/components/forms' },
|
|
114
|
+
{ path: 'keyfilter', symbols: { KeyFilterModule: 'MkMask', KeyFilter: 'MkMask' }, note: 'Use [mkMask] patterns or a Validators.pattern.', docs: '/components/utilities' },
|
|
115
|
+
{ path: 'inputmask', symbols: { InputMaskModule: 'MkMask', InputMask: 'MkMask' }, note: '[mkMask] on a native input (tokens 0 / A / *).', docs: '/components/utilities' },
|
|
116
|
+
{ path: 'ripple', symbols: { RippleModule: 'MkRipple', Ripple: 'MkRipple' }, docs: '/components/utilities' },
|
|
117
|
+
{ path: 'focustrap', symbols: { FocusTrapModule: 'MkFocusTrap', FocusTrap: 'MkFocusTrap' }, docs: '/core-services' },
|
|
118
|
+
{ path: 'autofocus', symbols: { AutoFocusModule: 'MkAutofocus', AutoFocus: 'MkAutofocus' }, docs: '/components/utilities' },
|
|
119
|
+
{ path: 'animateonscroll', symbols: { AnimateOnScrollModule: 'MkIntersect', AnimateOnScroll: 'MkIntersect' }, docs: '/components/utilities' },
|
|
120
|
+
{ path: 'styleclass', symbols: { StyleClassModule: null, StyleClass: null }, note: 'Bind [class.x] / @if instead.' },
|
|
121
|
+
{ path: 'dragdrop', symbols: { DragDropModule: 'MkDropList', Draggable: 'MkDrag', Droppable: 'MkDropList' }, docs: '/components/drag-drop' },
|
|
122
|
+
{ path: 'api', symbols: { MessageService: 'MkToastService', ConfirmationService: 'MkDialogService', MenuItem: null, PrimeNGConfig: null, PrimeIcons: null, FilterService: null, SortEvent: null, TreeNode: null, SelectItem: null, PrimeTemplate: null, SharedModule: null }, note: 'MessageService.add({severity, summary, detail}) → MkToastService.<severity>(detail, { title: summary }). ConfirmationService.confirm({…}) → await MkDialogService.confirm({ title, message, tone }).', docs: '/components/snackbar' },
|
|
123
|
+
{ path: 'config', symbols: { PrimeNG: null, providePrimeNG: null }, note: 'providePrimeNG({ theme }) → import @mk-kit/ui/styles.css and use provideMkI18n for strings.', docs: '/theming' },
|
|
124
|
+
];
|
|
125
|
+
/** Element / attribute rewrites for templates. Order matters only for overlapping prefixes (longest first is enforced by the transform). */
|
|
126
|
+
exports.SELECTORS = [
|
|
127
|
+
// Buttons
|
|
128
|
+
{ kind: 'attribute', from: 'pButton', to: 'mkButton', attrs: { severity: 'tone', '[severity]': '[tone]', '[outlined]': null, '[loading]': '[loading]', '[disabled]': '[disabled]' }, note: 'label="…" / icon="…" become content: <button mkButton><mk-icon name="…" /> Label</button>; [outlined]/[text]/[link] → variant="outline" | "ghost" | "link".', manual: true, docs: '/components/buttons' },
|
|
129
|
+
{ kind: 'element', from: 'p-button', note: '<p-button label="…"> → <button mkButton>…</button>; severity → tone; [outlined] → variant="outline".', docs: '/components/buttons' },
|
|
130
|
+
{ kind: 'element', from: 'p-splitbutton', to: 'mk-split-button', manual: true, note: '[model] → an <mk-menu #m> with mk-menu-item children and [menu]="m"; label → content.', docs: '/components/buttons' },
|
|
131
|
+
{ kind: 'element', from: 'p-splitButton', to: 'mk-split-button', manual: true, note: '[model] → an <mk-menu #m> with mk-menu-item children and [menu]="m"; label → content.', docs: '/components/buttons' },
|
|
132
|
+
// Text inputs
|
|
133
|
+
{ kind: 'attribute', from: 'pInputText', to: 'mkInput', docs: '/components/text-inputs' },
|
|
134
|
+
{ kind: 'attribute', from: 'pTextarea', to: 'mkInput', docs: '/components/text-inputs' },
|
|
135
|
+
{ kind: 'attribute', from: 'pInputTextarea', to: 'mkInput', docs: '/components/text-inputs' },
|
|
136
|
+
{ kind: 'attribute', from: 'pAutoFocus', to: 'mkAutofocus', docs: '/components/utilities' },
|
|
137
|
+
{ kind: 'attribute', from: 'pRipple', to: 'mkRipple', docs: '/components/utilities' },
|
|
138
|
+
{ kind: 'attribute', from: 'pTooltip', to: 'mkTooltip', attrs: { tooltipPosition: 'mkTooltipPlacement', '[tooltipPosition]': '[mkTooltipPlacement]' }, docs: '/components/popovers' },
|
|
139
|
+
{ kind: 'attribute', from: 'pBadge', note: 'pBadge attribute → place an <mk-badge dot> / <mk-badge>{{ n }}</mk-badge> next to the element.', docs: '/components/badges-avatars' },
|
|
140
|
+
{ kind: 'attribute', from: 'pKeyFilter', note: 'Use [mkMask] or Validators.pattern.', docs: '/components/utilities' },
|
|
141
|
+
{ kind: 'attribute', from: 'pInputMask', to: 'mkMask', manual: true, note: 'Mask tokens: PrimeNG 9/a/* → mk-kit 0/A/*.', docs: '/components/utilities' },
|
|
142
|
+
{ kind: 'element', from: 'p-inputnumber', to: 'mk-number-input', manual: true, note: 'mode="currency" → mk-currency-input; [showButtons] is always on.', docs: '/components/text-inputs' },
|
|
143
|
+
{ kind: 'element', from: 'p-inputNumber', to: 'mk-number-input', manual: true, note: 'mode="currency" → mk-currency-input; [showButtons] is always on.', docs: '/components/text-inputs' },
|
|
144
|
+
{ kind: 'element', from: 'p-password', to: 'mk-password-input', attrs: { '[feedback]': '[showStrength]', '[toggleMask]': null }, docs: '/components/text-inputs' },
|
|
145
|
+
{ kind: 'element', from: 'p-inputotp', to: 'mk-otp', docs: '/components/text-inputs' },
|
|
146
|
+
{ kind: 'element', from: 'p-inputOtp', to: 'mk-otp', docs: '/components/text-inputs' },
|
|
147
|
+
{ kind: 'element', from: 'p-floatlabel', to: 'mk-form-field', manual: true, note: 'Add labelPosition="float" and move the <label> text into [label].', docs: '/components/forms' },
|
|
148
|
+
{ kind: 'element', from: 'p-floatLabel', to: 'mk-form-field', manual: true, note: 'Add labelPosition="float" and move the <label> text into [label].', docs: '/components/forms' },
|
|
149
|
+
{ kind: 'element', from: 'p-iconfield', to: 'mk-input-group', manual: true, note: 'Icon → a prefix slot inside mk-input-group.', docs: '/components/text-inputs' },
|
|
150
|
+
{ kind: 'element', from: 'p-iconField', to: 'mk-input-group', manual: true, note: 'Icon → a prefix slot inside mk-input-group.', docs: '/components/text-inputs' },
|
|
151
|
+
{ kind: 'element', from: 'p-inputgroupaddon', note: 'Addons are content slots of mk-input-group: replace the wrapper with a plain element inside mk-input-group.', docs: '/components/text-inputs' },
|
|
152
|
+
{ kind: 'element', from: 'p-inputGroupAddon', note: 'Addons are content slots of mk-input-group: replace the wrapper with a plain element inside mk-input-group.', docs: '/components/text-inputs' },
|
|
153
|
+
{ kind: 'element', from: 'p-inputgroup', to: 'mk-input-group', docs: '/components/text-inputs' },
|
|
154
|
+
{ kind: 'element', from: 'p-inputGroup', to: 'mk-input-group', docs: '/components/text-inputs' },
|
|
155
|
+
// Toggles & selection
|
|
156
|
+
{ kind: 'element', from: 'p-checkbox', to: 'mk-checkbox', manual: true, note: 'Non-binary (array) checkboxes → one mk-checkbox per option; label="…" → content.', docs: '/components/toggles' },
|
|
157
|
+
{ kind: 'element', from: 'p-radiobutton', note: 'Wrap options in <mk-radio-group [(ngModel)]> with <mk-radio value="…">Label</mk-radio> children.', docs: '/components/toggles' },
|
|
158
|
+
{ kind: 'element', from: 'p-radioButton', note: 'Wrap options in <mk-radio-group [(ngModel)]> with <mk-radio value="…">Label</mk-radio> children.', docs: '/components/toggles' },
|
|
159
|
+
{ kind: 'element', from: 'p-toggleswitch', to: 'mk-switch', docs: '/components/toggles' },
|
|
160
|
+
{ kind: 'element', from: 'p-toggleSwitch', to: 'mk-switch', docs: '/components/toggles' },
|
|
161
|
+
{ kind: 'element', from: 'p-inputswitch', to: 'mk-switch', docs: '/components/toggles' },
|
|
162
|
+
{ kind: 'element', from: 'p-inputSwitch', to: 'mk-switch', docs: '/components/toggles' },
|
|
163
|
+
{ kind: 'element', from: 'p-select', to: 'mk-select', manual: true, note: 'Map [options] to {label, value}[] (no optionLabel/optionValue); [showClear] not needed.', docs: '/components/forms' },
|
|
164
|
+
{ kind: 'element', from: 'p-dropdown', to: 'mk-select', manual: true, note: 'Map [options] to {label, value}[] (no optionLabel/optionValue).', docs: '/components/forms' },
|
|
165
|
+
{ kind: 'element', from: 'p-multiselect', to: 'mk-multi-select', manual: true, note: 'Map [options] to {label, value}[].', docs: '/components/selection' },
|
|
166
|
+
{ kind: 'element', from: 'p-multiSelect', to: 'mk-multi-select', manual: true, note: 'Map [options] to {label, value}[].', docs: '/components/selection' },
|
|
167
|
+
{ kind: 'element', from: 'p-listbox', to: 'mk-listbox', manual: true, note: 'Map [options] to {label, value}[].', docs: '/components/selection' },
|
|
168
|
+
{ kind: 'element', from: 'p-autocomplete', to: 'mk-autocomplete', manual: true, note: '(completeMethod) + [suggestions] → [options] (array or (query) => Promise/Observable).', docs: '/components/selection' },
|
|
169
|
+
{ kind: 'element', from: 'p-autoComplete', to: 'mk-autocomplete', manual: true, note: '(completeMethod) + [suggestions] → [options] (array or (query) => Promise/Observable).', docs: '/components/selection' },
|
|
170
|
+
{ kind: 'element', from: 'p-cascadeselect', to: 'mk-cascader', manual: true, note: 'Options: {label, value, children}[].', docs: '/components/selection' },
|
|
171
|
+
{ kind: 'element', from: 'p-cascadeSelect', to: 'mk-cascader', manual: true, note: 'Options: {label, value, children}[].', docs: '/components/selection' },
|
|
172
|
+
{ kind: 'element', from: 'p-treeselect', to: 'mk-tree-select', manual: true, note: '[options] → [nodes] ({label, key, children}).', docs: '/components/selection' },
|
|
173
|
+
{ kind: 'element', from: 'p-treeSelect', to: 'mk-tree-select', manual: true, note: '[options] → [nodes] ({label, key, children}).', docs: '/components/selection' },
|
|
174
|
+
{ kind: 'element', from: 'p-selectbutton', to: 'mk-button-toggle-group', manual: true, note: '[options] → <mk-button-toggle value="…">Label</mk-button-toggle> children.', docs: '/components/selection' },
|
|
175
|
+
{ kind: 'element', from: 'p-selectButton', to: 'mk-button-toggle-group', manual: true, note: '[options] → <mk-button-toggle value="…">Label</mk-button-toggle> children.', docs: '/components/selection' },
|
|
176
|
+
{ kind: 'element', from: 'p-togglebutton', note: 'Single toggle → <mk-button-toggle-group> with one <mk-button-toggle>, or a checkbox-style mkButton.', docs: '/components/selection' },
|
|
177
|
+
{ kind: 'element', from: 'p-toggleButton', note: 'Single toggle → <mk-button-toggle-group> with one <mk-button-toggle>, or a checkbox-style mkButton.', docs: '/components/selection' },
|
|
178
|
+
{ kind: 'element', from: 'p-rating', to: 'mk-rating', attrs: { stars: 'max', '[stars]': '[max]' }, docs: '/components/sliders' },
|
|
179
|
+
{ kind: 'element', from: 'p-slider', to: 'mk-slider', manual: true, note: '[range] → mk-range-slider with [low, high] value.', docs: '/components/sliders' },
|
|
180
|
+
{ kind: 'element', from: 'p-colorpicker', to: 'mk-color-picker', docs: '/components/sliders' },
|
|
181
|
+
{ kind: 'element', from: 'p-colorPicker', to: 'mk-color-picker', docs: '/components/sliders' },
|
|
182
|
+
{ kind: 'element', from: 'p-knob', note: 'Display → mk-progress-ring [value]; editable → mk-slider.', docs: '/components/proportion-charts' },
|
|
183
|
+
// Dates
|
|
184
|
+
{ kind: 'element', from: 'p-datepicker', to: 'mk-date-picker', manual: true, note: '[minDate]/[maxDate] → [min]/[max]; [showTime] → mk-datetime-picker; [selectionMode]="range" → mk-date-range-picker; dateFormat → displayFormat (yyyy-MM-dd tokens).', docs: '/components/date-time' },
|
|
185
|
+
{ kind: 'element', from: 'p-datePicker', to: 'mk-date-picker', manual: true, note: '[minDate]/[maxDate] → [min]/[max]; [showTime] → mk-datetime-picker; range → mk-date-range-picker.', docs: '/components/date-time' },
|
|
186
|
+
{ kind: 'element', from: 'p-calendar', to: 'mk-date-picker', manual: true, note: '[minDate]/[maxDate] → [min]/[max]; [showTime] → mk-datetime-picker; range → mk-date-range-picker; [timeOnly] → mk-time-picker.', docs: '/components/date-time' },
|
|
187
|
+
// Files & editors
|
|
188
|
+
{ kind: 'element', from: 'p-fileupload', to: 'mk-file-upload', manual: true, note: '[url] + (onUpload) → [uploadFn]; [maxFileSize] → [maxSize]; mode="basic" is the default look.', docs: '/components/forms' },
|
|
189
|
+
{ kind: 'element', from: 'p-fileUpload', to: 'mk-file-upload', manual: true, note: '[url] + (onUpload) → [uploadFn]; [maxFileSize] → [maxSize].', docs: '/components/forms' },
|
|
190
|
+
{ kind: 'element', from: 'p-editor', to: 'mk-rich-text', manual: true, note: 'HTML in/out via [(ngModel)] works; toolbar customisation differs (see docs).', docs: '/components/rich-text' },
|
|
191
|
+
// Data
|
|
192
|
+
{ kind: 'element', from: 'p-table', to: 'mk-table', manual: true, note: '[value] → [data]; define [columns] ({key, header, sortable}) and drop the <ng-template pTemplate="header|body"> blocks (custom cells → mkTableCell); [paginator] → mk-pagination or MkTableDataSource; selection → [selectable] + [(selected)].', attrs: { '[value]': '[data]', '[selectionMode]': null, '[rowHover]': '[hover]', '[striped]': '[zebra]', '[stripedRows]': '[zebra]' }, docs: '/components/table' },
|
|
193
|
+
{ kind: 'element', from: 'p-treetable', to: 'mk-table', manual: true, note: 'mk-table with [childrenKey]="\'children\'"; same column mapping as p-table.', attrs: { '[value]': '[data]' }, docs: '/components/table' },
|
|
194
|
+
{ kind: 'element', from: 'p-treeTable', to: 'mk-table', manual: true, note: 'mk-table with [childrenKey]="\'children\'"; same column mapping as p-table.', attrs: { '[value]': '[data]' }, docs: '/components/table' },
|
|
195
|
+
{ kind: 'element', from: 'p-paginator', to: 'mk-pagination', manual: true, note: '[rows] → [pageSize], [totalRecords] → [total], [first] → 1-based [(page)]; (onPageChange) → (pageChange).', attrs: { '[rows]': '[pageSize]', '[totalRecords]': '[total]', '(onPageChange)': '(pageChange)' }, docs: '/components/navigation' },
|
|
196
|
+
{ kind: 'element', from: 'p-dataview', note: 'Compose mk-list / mk-grid with @for; mk-pagination for paging.', docs: '/components/cards-lists' },
|
|
197
|
+
{ kind: 'element', from: 'p-dataView', note: 'Compose mk-list / mk-grid with @for; mk-pagination for paging.', docs: '/components/cards-lists' },
|
|
198
|
+
{ kind: 'element', from: 'p-tree', to: 'mk-tree', manual: true, note: '[value] → [nodes] ({label, key, children}); [(selection)] → [(selected)].', attrs: { '[value]': '[nodes]' }, docs: '/components/tree' },
|
|
199
|
+
{ kind: 'element', from: 'p-orderlist', to: 'mk-sortable-list', manual: true, note: '[value] → [(items)] with a projected row template.', docs: '/components/drag-drop' },
|
|
200
|
+
{ kind: 'element', from: 'p-orderList', to: 'mk-sortable-list', manual: true, note: '[value] → [(items)] with a projected row template.', docs: '/components/drag-drop' },
|
|
201
|
+
{ kind: 'element', from: 'p-picklist', to: 'mk-transfer-list', manual: true, note: '[source]/[target] → [items] + [(value)].', docs: '/components/selection' },
|
|
202
|
+
{ kind: 'element', from: 'p-pickList', to: 'mk-transfer-list', manual: true, note: '[source]/[target] → [items] + [(value)].', docs: '/components/selection' },
|
|
203
|
+
{ kind: 'element', from: 'p-scroller', to: 'mk-virtual-scroll', manual: true, note: '[itemSize] → [itemHeight]; row template is projected.', attrs: { '[itemSize]': '[itemHeight]' }, docs: '/components/data' },
|
|
204
|
+
{ kind: 'element', from: 'p-virtualscroller', to: 'mk-virtual-scroll', manual: true, note: '[itemSize] → [itemHeight]; row template is projected.', attrs: { '[itemSize]': '[itemHeight]' }, docs: '/components/data' },
|
|
205
|
+
{ kind: 'element', from: 'p-timeline', to: 'mk-timeline', manual: true, note: '[value] + templates → <mk-timeline-item> children.', docs: '/components/empty-timeline' },
|
|
206
|
+
{ kind: 'element', from: 'p-organizationchart', note: 'No org chart yet — mk-tree is the closest.', docs: '/components/tree' },
|
|
207
|
+
{ kind: 'element', from: 'p-organizationChart', note: 'No org chart yet — mk-tree is the closest.', docs: '/components/tree' },
|
|
208
|
+
{ kind: 'element', from: 'p-chart', note: 'Replace by the matching SVG chart: type="line" → mk-line-chart, "bar" → mk-bar-chart, "doughnut"/"pie" → mk-donut-chart, "radar" → mk-radar-chart, "scatter"/"bubble" → mk-scatter-chart. Data shape is plain series arrays.', docs: '/components/charts' },
|
|
209
|
+
{ kind: 'element', from: 'p-metergroup', note: 'No meter group yet; use one mk-progress-bar per segment.', docs: '/components/loading' },
|
|
210
|
+
{ kind: 'element', from: 'p-meterGroup', note: 'No meter group yet; use one mk-progress-bar per segment.', docs: '/components/loading' },
|
|
211
|
+
// Overlays & feedback
|
|
212
|
+
{ kind: 'element', from: 'p-dialog', note: 'mk-dialog is opened with MkDialogService.open(Component, { data }) — move this <p-dialog> body into a component (see docs) and drop [(visible)].', docs: '/components/dialogs' },
|
|
213
|
+
{ kind: 'element', from: 'p-confirmdialog', note: 'Remove this host element; call await MkDialogService.confirm({ title, message, tone }) where you used ConfirmationService.', docs: '/components/dialogs' },
|
|
214
|
+
{ kind: 'element', from: 'p-confirmDialog', note: 'Remove this host element; call await MkDialogService.confirm({ title, message, tone }) where you used ConfirmationService.', docs: '/components/dialogs' },
|
|
215
|
+
{ kind: 'element', from: 'p-confirmpopup', note: 'Put [mkPopconfirmFor]="pc" on the trigger and an <mk-popconfirm #pc message="…" (confirm)="…"> next to it.', docs: '/components/popovers' },
|
|
216
|
+
{ kind: 'element', from: 'p-confirmPopup', note: 'Put [mkPopconfirmFor]="pc" on the trigger and an <mk-popconfirm #pc message="…" (confirm)="…"> next to it.', docs: '/components/popovers' },
|
|
217
|
+
{ kind: 'element', from: 'p-toast', note: 'Remove this host element; MkToastService mounts its own container. MessageService.add({severity, summary, detail}) → toast.<severity>(detail, { title: summary }).', docs: '/components/snackbar' },
|
|
218
|
+
{ kind: 'element', from: 'p-message', to: 'mk-alert', manual: true, note: 'severity → tone (error → danger); text="…" → content.', attrs: { severity: 'tone', '[severity]': '[tone]' }, docs: '/components/feedback' },
|
|
219
|
+
{ kind: 'element', from: 'p-messages', note: 'Render one <mk-alert [tone]> per message with @for.', docs: '/components/feedback' },
|
|
220
|
+
{ kind: 'element', from: 'p-popover', note: 'Put [mkPopoverTriggerFor]="pop" on the trigger and rename this element to <mk-popover #pop>; drop #op.toggle($event).', docs: '/components/popovers' },
|
|
221
|
+
{ kind: 'element', from: 'p-overlaypanel', note: 'Put [mkPopoverTriggerFor]="pop" on the trigger and rename this element to <mk-popover #pop>; drop #op.toggle($event).', docs: '/components/popovers' },
|
|
222
|
+
{ kind: 'element', from: 'p-overlayPanel', note: 'Put [mkPopoverTriggerFor]="pop" on the trigger and rename this element to <mk-popover #pop>; drop #op.toggle($event).', docs: '/components/popovers' },
|
|
223
|
+
{ kind: 'element', from: 'p-drawer', to: 'mk-drawer', manual: true, note: '[(visible)] → [(open)]; position → side; [modal] → [hasBackdrop].', attrs: { '[(visible)]': '[(open)]', '[visible]': '[open]', '(visibleChange)': '(openChange)', position: 'side', '[position]': '[side]', '[modal]': '[hasBackdrop]', header: 'heading', '[header]': '[heading]' }, docs: '/components/drawer' },
|
|
224
|
+
{ kind: 'element', from: 'p-sidebar', to: 'mk-drawer', manual: true, note: '[(visible)] → [(open)]; position → side; [modal] → [hasBackdrop].', attrs: { '[(visible)]': '[(open)]', '[visible]': '[open]', '(visibleChange)': '(openChange)', position: 'side', '[position]': '[side]', '[modal]': '[hasBackdrop]', header: 'heading', '[header]': '[heading]' }, docs: '/components/drawer' },
|
|
225
|
+
{ kind: 'element', from: 'p-blockui', note: 'Use the [mkBlockUi]="busy()" directive on the region (or MkBlockUiService for the page).', docs: '/components/feedback' },
|
|
226
|
+
{ kind: 'element', from: 'p-blockUI', note: 'Use the [mkBlockUi]="busy()" directive on the region (or MkBlockUiService for the page).', docs: '/components/feedback' },
|
|
227
|
+
{ kind: 'element', from: 'p-progressbar', to: 'mk-progress-bar', attrs: { '[mode]': null, mode: null }, manual: true, note: 'mode="indeterminate" → the indeterminate attribute.', docs: '/components/loading' },
|
|
228
|
+
{ kind: 'element', from: 'p-progressBar', to: 'mk-progress-bar', manual: true, note: 'mode="indeterminate" → the indeterminate attribute.', docs: '/components/loading' },
|
|
229
|
+
{ kind: 'element', from: 'p-progressspinner', to: 'mk-spinner', docs: '/components/loading' },
|
|
230
|
+
{ kind: 'element', from: 'p-progressSpinner', to: 'mk-spinner', docs: '/components/loading' },
|
|
231
|
+
{ kind: 'element', from: 'p-skeleton', to: 'mk-skeleton', attrs: { shape: 'shape', width: 'width', height: 'height' }, docs: '/components/loading' },
|
|
232
|
+
{ kind: 'element', from: 'p-badge', to: 'mk-badge', manual: true, note: '[value] → content; severity → tone.', attrs: { severity: 'tone', '[severity]': '[tone]' }, docs: '/components/badges-avatars' },
|
|
233
|
+
{ kind: 'element', from: 'p-tag', to: 'mk-tag', manual: true, note: '[value] → content; severity → tone; [rounded] is the default look.', attrs: { severity: 'tone', '[severity]': '[tone]' }, docs: '/components/badges-avatars' },
|
|
234
|
+
{ kind: 'element', from: 'p-chip', to: 'mk-chip', manual: true, note: 'label="…" → content; [removable] is the same; (onRemove) → (remove).', attrs: { '(onRemove)': '(remove)' }, docs: '/components/badges-avatars' },
|
|
235
|
+
{ kind: 'element', from: 'p-avatar', to: 'mk-avatar', manual: true, note: 'label="AB" → name="A B" (initials are derived); image → src; shape="circle" is the default.', attrs: { image: 'src', '[image]': '[src]' }, docs: '/components/badges-avatars' },
|
|
236
|
+
{ kind: 'element', from: 'p-avatargroup', to: 'mk-avatar-group', docs: '/components/badges-avatars' },
|
|
237
|
+
{ kind: 'element', from: 'p-avatarGroup', to: 'mk-avatar-group', docs: '/components/badges-avatars' },
|
|
238
|
+
{ kind: 'element', from: 'p-card', to: 'mk-card', manual: true, note: 'header="…" → <mk-card-header> / <mk-card-title>; pTemplate="footer" → <mk-card-footer>.', docs: '/components/cards-lists' },
|
|
239
|
+
{ kind: 'element', from: 'p-panel', to: 'mk-card', manual: true, note: 'header → <mk-card-header>; [toggleable] → use mk-accordion-item instead.', docs: '/components/cards-lists' },
|
|
240
|
+
{ kind: 'element', from: 'p-fieldset', to: 'mk-card', manual: true, note: 'legend → <mk-card-header>.', docs: '/components/cards-lists' },
|
|
241
|
+
{ kind: 'element', from: 'p-divider', to: 'mk-divider', attrs: { layout: 'orientation', '[layout]': '[orientation]' }, docs: '/components/cards-lists' },
|
|
242
|
+
{ kind: 'element', from: 'p-toolbar', to: 'mk-toolbar', manual: true, note: 'pTemplate="start|end" → plain content; use mk-flex for alignment.', docs: '/components/structure' },
|
|
243
|
+
{ kind: 'element', from: 'p-splitter', to: 'mk-splitter', manual: true, note: 'p-splitterpanel children → two projected children; [layout] → orientation.', attrs: { layout: 'orientation', '[layout]': '[orientation]' }, docs: '/components/structure' },
|
|
244
|
+
{ kind: 'element', from: 'p-splitterpanel', note: 'Drop the wrapper: mk-splitter projects its two children directly.', docs: '/components/structure' },
|
|
245
|
+
{ kind: 'element', from: 'p-splitterPanel', note: 'Drop the wrapper: mk-splitter projects its two children directly.', docs: '/components/structure' },
|
|
246
|
+
{ kind: 'element', from: 'p-scrollpanel', to: 'mk-scroll-area', docs: '/components/structure' },
|
|
247
|
+
{ kind: 'element', from: 'p-scrollPanel', to: 'mk-scroll-area', docs: '/components/structure' },
|
|
248
|
+
{ kind: 'element', from: 'p-scrolltop', to: 'mk-back-to-top', docs: '/components/navigation' },
|
|
249
|
+
{ kind: 'element', from: 'p-scrollTop', to: 'mk-back-to-top', docs: '/components/navigation' },
|
|
250
|
+
{ kind: 'element', from: 'p-image', to: 'mk-image', manual: true, note: '[preview] → MkLightboxService.open().', docs: '/components/images' },
|
|
251
|
+
{ kind: 'element', from: 'p-galleria', to: 'mk-image-gallery', manual: true, note: '[value] → [images] ({src, alt}).', docs: '/components/images' },
|
|
252
|
+
{ kind: 'element', from: 'p-carousel', to: 'mk-carousel', manual: true, note: '[value] + template → <ng-template mkCarouselSlide> per item.', docs: '/components/data' },
|
|
253
|
+
{ kind: 'element', from: 'p-inplace', to: 'mk-inline-edit', manual: true, note: 'Bind [(value)] instead of display/content templates.', docs: '/components/table' },
|
|
254
|
+
// Navigation
|
|
255
|
+
{ kind: 'element', from: 'p-tabs', to: 'mk-tabs', manual: true, note: 'Merge each <p-tab> + <p-tabpanel> pair into <mk-tab label="…">…</mk-tab>; drop p-tablist / p-tabpanels.', docs: '/components/navigation' },
|
|
256
|
+
{ kind: 'element', from: 'p-tablist', note: 'Drop: mk-tabs renders its own tab list.', docs: '/components/navigation' },
|
|
257
|
+
{ kind: 'element', from: 'p-tabpanels', note: 'Drop: <mk-tab> children go directly inside <mk-tabs>.', docs: '/components/navigation' },
|
|
258
|
+
{ kind: 'element', from: 'p-tabpanel', note: 'Becomes <mk-tab label="…"> (label from the matching <p-tab>).', docs: '/components/navigation' },
|
|
259
|
+
{ kind: 'element', from: 'p-tabview', to: 'mk-tabs', manual: true, note: '[(activeIndex)] → [(selectedIndex)].', attrs: { '[(activeIndex)]': '[(selectedIndex)]', '[activeIndex]': '[selectedIndex]', '(activeIndexChange)': '(selectedIndexChange)' }, docs: '/components/navigation' },
|
|
260
|
+
{ kind: 'element', from: 'p-tabView', to: 'mk-tabs', manual: true, note: '[(activeIndex)] → [(selectedIndex)].', attrs: { '[(activeIndex)]': '[(selectedIndex)]', '[activeIndex]': '[selectedIndex]', '(activeIndexChange)': '(selectedIndexChange)' }, docs: '/components/navigation' },
|
|
261
|
+
{ kind: 'element', from: 'p-tabPanel', to: 'mk-tab', attrs: { header: 'label', '[header]': '[label]' }, docs: '/components/navigation' },
|
|
262
|
+
{ kind: 'element', from: 'p-accordion', to: 'mk-accordion', attrs: { '[multiple]': '[multi]', multiple: 'multi' }, docs: '/components/navigation' },
|
|
263
|
+
{ kind: 'element', from: 'p-accordion-panel', to: 'mk-accordion-item', manual: true, note: 'Move the <p-accordion-header> text into header="…" and drop the p-accordion-content wrapper.', docs: '/components/navigation' },
|
|
264
|
+
{ kind: 'element', from: 'p-accordionpanel', to: 'mk-accordion-item', manual: true, note: 'Move the <p-accordion-header> text into header="…" and drop the p-accordion-content wrapper.', docs: '/components/navigation' },
|
|
265
|
+
{ kind: 'element', from: 'p-accordion-header', note: 'Becomes header="…" on the mk-accordion-item (or an [mkAccordionHeader] template).', docs: '/components/navigation' },
|
|
266
|
+
{ kind: 'element', from: 'p-accordion-content', note: 'Drop the wrapper: content goes directly inside mk-accordion-item.', docs: '/components/navigation' },
|
|
267
|
+
{ kind: 'element', from: 'p-accordiontab', to: 'mk-accordion-item', docs: '/components/navigation' },
|
|
268
|
+
{ kind: 'element', from: 'p-accordionTab', to: 'mk-accordion-item', docs: '/components/navigation' },
|
|
269
|
+
{ kind: 'element', from: 'p-stepper', to: 'mk-stepper', manual: true, note: 'Each p-step + p-step-panel → <mk-step label="…">; [(value)] → [(selectedIndex)] (0-based).', docs: '/components/stepper' },
|
|
270
|
+
{ kind: 'element', from: 'p-steps', to: 'mk-stepper', manual: true, note: '[model] → <mk-step label="…"> children; [(activeIndex)] → [(selectedIndex)].', attrs: { '[(activeIndex)]': '[(selectedIndex)]', '[activeIndex]': '[selectedIndex]' }, docs: '/components/stepper' },
|
|
271
|
+
{ kind: 'element', from: 'p-menu', to: 'mk-menu', manual: true, note: '[model] → <mk-menu-item> children; [popup] + #menu.toggle($event) → [mkMenuTriggerFor]="menu" on the button.', docs: '/components/navigation' },
|
|
272
|
+
{ kind: 'element', from: 'p-tieredmenu', to: 'mk-menu', manual: true, note: '[model] → <mk-menu-item> children; nested items via [mkSubmenuFor].', docs: '/components/navigation' },
|
|
273
|
+
{ kind: 'element', from: 'p-tieredMenu', to: 'mk-menu', manual: true, note: '[model] → <mk-menu-item> children; nested items via [mkSubmenuFor].', docs: '/components/navigation' },
|
|
274
|
+
{ kind: 'element', from: 'p-contextmenu', to: 'mk-menu', manual: true, note: '[model] → <mk-menu-item> children; [target] → put [mkContextMenuTriggerFor]="menu" on the element.', docs: '/components/context-menu' },
|
|
275
|
+
{ kind: 'element', from: 'p-contextMenu', to: 'mk-menu', manual: true, note: '[model] → <mk-menu-item> children; [target] → put [mkContextMenuTriggerFor]="menu" on the element.', docs: '/components/context-menu' },
|
|
276
|
+
{ kind: 'element', from: 'p-menubar', note: 'Horizontal nav → <mk-toolbar> with mkButton links, or mk-nav-list.', docs: '/components/command-nav' },
|
|
277
|
+
{ kind: 'element', from: 'p-megamenu', note: 'No mega menu; compose mk-popover + mk-nav-list.', docs: '/components/command-nav' },
|
|
278
|
+
{ kind: 'element', from: 'p-megaMenu', note: 'No mega menu; compose mk-popover + mk-nav-list.', docs: '/components/command-nav' },
|
|
279
|
+
{ kind: 'element', from: 'p-panelmenu', to: 'mk-nav-list', manual: true, note: '[model] → <mk-nav-group> / <mk-nav-item> children.', docs: '/components/command-nav' },
|
|
280
|
+
{ kind: 'element', from: 'p-panelMenu', to: 'mk-nav-list', manual: true, note: '[model] → <mk-nav-group> / <mk-nav-item> children.', docs: '/components/command-nav' },
|
|
281
|
+
{ kind: 'element', from: 'p-breadcrumb', to: 'mk-breadcrumb', manual: true, note: '[model] → <mk-breadcrumb-item> children.', docs: '/components/navigation' },
|
|
282
|
+
{ kind: 'element', from: 'p-dock', note: 'No dock.', docs: '/components/navigation' },
|
|
283
|
+
{ kind: 'element', from: 'p-speeddial', to: 'mk-fab', manual: true, note: '[model] → <button mkFabAction> children.', docs: '/components/navigation' },
|
|
284
|
+
{ kind: 'element', from: 'p-speedDial', to: 'mk-fab', manual: true, note: '[model] → <button mkFabAction> children.', docs: '/components/navigation' },
|
|
285
|
+
{ kind: 'element', from: 'p-terminal', note: 'No terminal.', docs: '/components/data' },
|
|
286
|
+
];
|
|
287
|
+
exports.DOCS_BASE = D;
|
|
288
|
+
/** package.json dependencies the migration makes obsolete. */
|
|
289
|
+
exports.PRIMENG_PACKAGES = ['primeng', '@primeng/themes', '@primeuix/themes', '@primeuix/styled', 'primeicons', 'primeflex'];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"$id": "MkKitMigratePrimeng",
|
|
4
|
+
"title": "@mk-kit/ui migrate-primeng schematic",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"path": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"default": "src",
|
|
10
|
+
"description": "Directory to migrate (TypeScript and HTML files are visited recursively)."
|
|
11
|
+
},
|
|
12
|
+
"dryRun": {
|
|
13
|
+
"type": "boolean",
|
|
14
|
+
"default": false,
|
|
15
|
+
"description": "Compute and print the report without writing any file.",
|
|
16
|
+
"alias": "d"
|
|
17
|
+
},
|
|
18
|
+
"report": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"default": "primeng-migration.md",
|
|
21
|
+
"description": "Path of the Markdown report to write (relative to the workspace root)."
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"required": []
|
|
25
|
+
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MK_STYLE_PATH = void 0;
|
|
4
|
+
exports.transformTypeScript = transformTypeScript;
|
|
5
|
+
exports.transformTemplate = transformTemplate;
|
|
6
|
+
exports.transformAngularJson = transformAngularJson;
|
|
7
|
+
exports.transformPackageJson = transformPackageJson;
|
|
8
|
+
exports.renderReport = renderReport;
|
|
9
|
+
/**
|
|
10
|
+
* Pure string transforms behind the `migrate-primeng` schematic. No devkit
|
|
11
|
+
* types here so they can be unit-tested on fixtures and reused by other
|
|
12
|
+
* tooling. Every function returns the new text plus what it did.
|
|
13
|
+
*/
|
|
14
|
+
const mapping_1 = require("./mapping");
|
|
15
|
+
const MODULE_BY_PATH = new Map(mapping_1.MODULES.map((m) => [m.path, m]));
|
|
16
|
+
function escapeRe(s) {
|
|
17
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
18
|
+
}
|
|
19
|
+
function docsUrl(route) {
|
|
20
|
+
return route ? `${mapping_1.DOCS_BASE}${route}` : undefined;
|
|
21
|
+
}
|
|
22
|
+
/* ------------------------------------------------------------------------ */
|
|
23
|
+
/* TypeScript */
|
|
24
|
+
/* ------------------------------------------------------------------------ */
|
|
25
|
+
const IMPORT_RE = /import\s*(type\s+)?\{([^}]*)\}\s*from\s*['"]primeng\/([\w-]+)['"];?[ \t]*\r?\n?/g;
|
|
26
|
+
/**
|
|
27
|
+
* Rewrites `primeng/*` imports to `@mk-kit/ui`, renames the imported
|
|
28
|
+
* identifiers everywhere in the file, keeps unmapped symbols on their
|
|
29
|
+
* original import, and dedupes `imports: [...]` arrays.
|
|
30
|
+
*/
|
|
31
|
+
function transformTypeScript(source) {
|
|
32
|
+
const findings = [];
|
|
33
|
+
const mkNames = new Set();
|
|
34
|
+
const renames = new Map();
|
|
35
|
+
const keep = [];
|
|
36
|
+
let text = source;
|
|
37
|
+
let firstImportIndex = -1;
|
|
38
|
+
text = text.replace(IMPORT_RE, (whole, isType, names, path, offset) => {
|
|
39
|
+
const rule = MODULE_BY_PATH.get(path);
|
|
40
|
+
if (firstImportIndex === -1)
|
|
41
|
+
firstImportIndex = offset;
|
|
42
|
+
const symbols = names
|
|
43
|
+
.split(',')
|
|
44
|
+
.map((n) => n.trim())
|
|
45
|
+
.filter(Boolean);
|
|
46
|
+
const unmapped = [];
|
|
47
|
+
for (const raw of symbols) {
|
|
48
|
+
const [name, alias] = raw.split(/\s+as\s+/).map((s) => s.trim());
|
|
49
|
+
const target = rule?.symbols[name];
|
|
50
|
+
if (target) {
|
|
51
|
+
mkNames.add(target);
|
|
52
|
+
renames.set(alias ?? name, target);
|
|
53
|
+
add(findings, `import:primeng/${path}`, `${name} → ${target}`, 'rewrite', rule?.docs);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
unmapped.push(raw);
|
|
57
|
+
add(findings, `import:primeng/${path}`, `${name} (primeng/${path}) has no drop-in equivalent — kept`, rule ? 'manual' : 'unmapped', rule?.docs);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (unmapped.length)
|
|
61
|
+
keep.push(`import ${isType ?? ''}{ ${unmapped.join(', ')} } from 'primeng/${path}';\n`);
|
|
62
|
+
if (rule?.note && symbols.some((s) => rule.symbols[s.split(/\s+as\s+/)[0].trim()])) {
|
|
63
|
+
add(findings, `note:primeng/${path}`, rule.note, 'manual', rule.docs);
|
|
64
|
+
}
|
|
65
|
+
return '';
|
|
66
|
+
});
|
|
67
|
+
if (firstImportIndex === -1)
|
|
68
|
+
return { text: source, changed: false, findings };
|
|
69
|
+
// Rename identifiers (whole words) — includes `imports: [...]` entries and injected services.
|
|
70
|
+
for (const [from, to] of renames) {
|
|
71
|
+
if (from === to)
|
|
72
|
+
continue;
|
|
73
|
+
text = text.replace(new RegExp(`\\b${escapeRe(from)}\\b`, 'g'), to);
|
|
74
|
+
}
|
|
75
|
+
// Dedupe entries inside `imports: [ ... ]` arrays (several PrimeNG symbols map to one class).
|
|
76
|
+
text = text.replace(/imports:\s*\[([^\]]*)\]/g, (whole, inner) => {
|
|
77
|
+
const seen = new Set();
|
|
78
|
+
const items = inner
|
|
79
|
+
.split(',')
|
|
80
|
+
.map((s) => s.trim())
|
|
81
|
+
.filter((s) => s && !seen.has(s) && seen.add(s));
|
|
82
|
+
const multiline = inner.includes('\n');
|
|
83
|
+
return multiline ? `imports: [\n ${items.join(',\n ')},\n ]` : `imports: [${items.join(', ')}]`;
|
|
84
|
+
});
|
|
85
|
+
// Insert the mk-kit import where the first primeng import was, merging with an existing one.
|
|
86
|
+
const header = [...(keep.length ? keep : [])];
|
|
87
|
+
if (mkNames.size) {
|
|
88
|
+
const existing = /import\s*\{([^}]*)\}\s*from\s*['"]@mk-kit\/ui['"];?[ \t]*\r?\n?/;
|
|
89
|
+
const m = existing.exec(text);
|
|
90
|
+
if (m) {
|
|
91
|
+
const merged = new Set(m[1].split(',').map((s) => s.trim()).filter(Boolean));
|
|
92
|
+
for (const n of mkNames)
|
|
93
|
+
merged.add(n);
|
|
94
|
+
text = text.replace(existing, `import { ${[...merged].join(', ')} } from '@mk-kit/ui';\n`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
header.unshift(`import { ${[...mkNames].join(', ')} } from '@mk-kit/ui';\n`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
text = text.slice(0, firstImportIndex) + header.join('') + text.slice(firstImportIndex);
|
|
101
|
+
// Service usage hints (MessageService / ConfirmationService bodies are not mechanical).
|
|
102
|
+
if (renames.has('MessageService')) {
|
|
103
|
+
text = text.replace(/(\n[ \t]*)([^\n]*\.add\(\s*\{[^\n]*severity[^\n]*)/g, '$1// mk-kit: MessageService.add({severity, summary, detail}) → toast.<severity>(detail, { title: summary })$1$2');
|
|
104
|
+
}
|
|
105
|
+
if (renames.has('ConfirmationService')) {
|
|
106
|
+
text = text.replace(/(\n[ \t]*)([^\n]*\.confirm\(\s*\{[^\n]*)/g, '$1// mk-kit: ConfirmationService.confirm({...}) → await dialog.confirm({ title, message, tone }) returns boolean$1$2');
|
|
107
|
+
}
|
|
108
|
+
// Inline templates get the template pass too.
|
|
109
|
+
const tpl = transformTemplate(text, true);
|
|
110
|
+
text = tpl.text;
|
|
111
|
+
findings.push(...tpl.findings);
|
|
112
|
+
return { text, changed: text !== source, findings };
|
|
113
|
+
}
|
|
114
|
+
/* ------------------------------------------------------------------------ */
|
|
115
|
+
/* Templates */
|
|
116
|
+
/* ------------------------------------------------------------------------ */
|
|
117
|
+
/** Longest selectors first so `p-inputgroupaddon` is not eaten by `p-inputgroup`. */
|
|
118
|
+
const ELEMENT_RULES = mapping_1.SELECTORS.filter((r) => r.kind === 'element').sort((a, b) => b.from.length - a.from.length);
|
|
119
|
+
const ATTRIBUTE_RULES = mapping_1.SELECTORS.filter((r) => r.kind === 'attribute').sort((a, b) => b.from.length - a.from.length);
|
|
120
|
+
function noteComment(rule, inline) {
|
|
121
|
+
const text = `mk-kit: ${rule.from} → ${rule.to ?? 'manual'} — ${rule.note ?? ''}`.trim();
|
|
122
|
+
// Inside a TS template literal `-->` is fine; keep one comment style everywhere.
|
|
123
|
+
return inline ? `<!-- ${text} -->` : `<!-- ${text} -->`;
|
|
124
|
+
}
|
|
125
|
+
function renameAttrs(openTag, attrs) {
|
|
126
|
+
if (!attrs)
|
|
127
|
+
return openTag;
|
|
128
|
+
let out = openTag;
|
|
129
|
+
for (const [from, to] of Object.entries(attrs)) {
|
|
130
|
+
const re = new RegExp(`(\\s)${escapeRe(from)}(?=[\\s=>/])`, 'g');
|
|
131
|
+
out = out.replace(re, (_m, ws) => (to === null ? ws.trimEnd() : `${ws}${to}`));
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Rewrites PrimeNG selectors in a template (an .html file, or a whole .ts file
|
|
137
|
+
* when `inline` — only `<p-…` tags and known attribute tokens are touched).
|
|
138
|
+
*/
|
|
139
|
+
function transformTemplate(source, inline = false) {
|
|
140
|
+
const findings = [];
|
|
141
|
+
let text = source;
|
|
142
|
+
for (const rule of ELEMENT_RULES) {
|
|
143
|
+
const open = new RegExp(`<${escapeRe(rule.from)}(?=[\\s>/])`, 'g');
|
|
144
|
+
const close = new RegExp(`</${escapeRe(rule.from)}\\s*>`, 'g');
|
|
145
|
+
const matches = text.match(open);
|
|
146
|
+
if (!matches)
|
|
147
|
+
continue;
|
|
148
|
+
const n = matches.length;
|
|
149
|
+
if (rule.to) {
|
|
150
|
+
// Rename open/close tags, then attributes on each opening tag.
|
|
151
|
+
text = text.replace(open, `<${rule.to}`).replace(close, `</${rule.to}>`);
|
|
152
|
+
if (rule.attrs) {
|
|
153
|
+
const tagRe = new RegExp(`<${escapeRe(rule.to)}(\\s[^>]*)?>`, 'g');
|
|
154
|
+
text = text.replace(tagRe, (tag) => renameAttrs(tag, rule.attrs));
|
|
155
|
+
}
|
|
156
|
+
add(findings, `element:${rule.from}`, `<${rule.from}> → <${rule.to}>`, 'rewrite', rule.docs, n);
|
|
157
|
+
if (rule.manual && rule.note) {
|
|
158
|
+
text = insertNote(text, `<${rule.to}`, noteComment(rule, inline));
|
|
159
|
+
add(findings, `manual:${rule.from}`, rule.note, 'manual', rule.docs, n);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
text = insertNote(text, `<${rule.from}`, noteComment(rule, inline));
|
|
164
|
+
add(findings, `element:${rule.from}`, rule.note ?? 'no drop-in equivalent', 'unmapped', rule.docs, n);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
for (const rule of ATTRIBUTE_RULES) {
|
|
168
|
+
// Attribute token forms: `pButton`, `[pTooltip]="…"`, `pTooltip="…"`.
|
|
169
|
+
const token = new RegExp(`(?<=[\\s\\[])${escapeRe(rule.from)}(?=[\\s=\\]>/])`, 'g');
|
|
170
|
+
const matches = text.match(token);
|
|
171
|
+
if (!matches)
|
|
172
|
+
continue;
|
|
173
|
+
const n = matches.length;
|
|
174
|
+
if (rule.to) {
|
|
175
|
+
text = text.replace(token, rule.to);
|
|
176
|
+
if (rule.attrs) {
|
|
177
|
+
const tagRe = new RegExp(`<[a-zA-Z][^>]*\\b${escapeRe(rule.to)}\\b[^>]*>`, 'g');
|
|
178
|
+
text = text.replace(tagRe, (tag) => renameAttrs(tag, rule.attrs));
|
|
179
|
+
}
|
|
180
|
+
add(findings, `attr:${rule.from}`, `${rule.from} → ${rule.to}`, 'rewrite', rule.docs, n);
|
|
181
|
+
if (rule.manual && rule.note) {
|
|
182
|
+
text = insertNoteBeforeTagWith(text, rule.to, noteComment(rule, inline));
|
|
183
|
+
add(findings, `manual:${rule.from}`, rule.note, 'manual', rule.docs, n);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
text = insertNoteBeforeTagWith(text, rule.from, noteComment(rule, inline));
|
|
188
|
+
add(findings, `attr:${rule.from}`, rule.note ?? 'no drop-in equivalent', 'unmapped', rule.docs, n);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return { text, changed: text !== source, findings };
|
|
192
|
+
}
|
|
193
|
+
/** Insert `note` once, right before the first occurrence of `needle` (keeps indentation). */
|
|
194
|
+
function insertNote(text, needle, note) {
|
|
195
|
+
if (text.includes(note))
|
|
196
|
+
return text;
|
|
197
|
+
const i = text.indexOf(needle);
|
|
198
|
+
if (i === -1)
|
|
199
|
+
return text;
|
|
200
|
+
const lineStart = text.lastIndexOf('\n', i) + 1;
|
|
201
|
+
const indent = text.slice(lineStart, i).match(/^[ \t]*$/) ? text.slice(lineStart, i) : '';
|
|
202
|
+
return indent ? text.slice(0, lineStart) + indent + note + '\n' + text.slice(lineStart) : text.slice(0, i) + note + ' ' + text.slice(i);
|
|
203
|
+
}
|
|
204
|
+
/** Insert `note` once before the first tag that carries `attr`. */
|
|
205
|
+
function insertNoteBeforeTagWith(text, attr, note) {
|
|
206
|
+
if (text.includes(note))
|
|
207
|
+
return text;
|
|
208
|
+
const re = new RegExp(`<[a-zA-Z][^>]*(?<=[\\s\\[])${escapeRe(attr)}(?=[\\s=\\]>/])`);
|
|
209
|
+
const m = re.exec(text);
|
|
210
|
+
if (!m)
|
|
211
|
+
return text;
|
|
212
|
+
return insertNote(text, m[0], note);
|
|
213
|
+
}
|
|
214
|
+
/* ------------------------------------------------------------------------ */
|
|
215
|
+
/* Workspace files */
|
|
216
|
+
/* ------------------------------------------------------------------------ */
|
|
217
|
+
exports.MK_STYLE_PATH = 'node_modules/@mk-kit/ui/styles/mk-kit.css';
|
|
218
|
+
/** Remove PrimeNG / primeicons / primeflex style entries from every project's `styles`; add the mk-kit theme. */
|
|
219
|
+
function transformAngularJson(source) {
|
|
220
|
+
const findings = [];
|
|
221
|
+
let json;
|
|
222
|
+
try {
|
|
223
|
+
json = JSON.parse(source);
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
return { text: source, changed: false, findings };
|
|
227
|
+
}
|
|
228
|
+
let changed = false;
|
|
229
|
+
for (const project of Object.values(json.projects ?? {})) {
|
|
230
|
+
const options = project?.architect?.build?.options;
|
|
231
|
+
if (!options || !Array.isArray(options.styles))
|
|
232
|
+
continue;
|
|
233
|
+
const before = options.styles.length;
|
|
234
|
+
options.styles = options.styles.filter((s) => {
|
|
235
|
+
const p = typeof s === 'string' ? s : s?.input ?? '';
|
|
236
|
+
return !/prime(ng|icons|flex|uix)/i.test(p);
|
|
237
|
+
});
|
|
238
|
+
if (options.styles.length !== before) {
|
|
239
|
+
changed = true;
|
|
240
|
+
add(findings, 'styles:primeng', `removed ${before - options.styles.length} PrimeNG/primeicons style entries`, 'rewrite', '/theming', before - options.styles.length);
|
|
241
|
+
}
|
|
242
|
+
const has = options.styles.some((s) => (typeof s === 'string' ? s : s?.input ?? '').includes('@mk-kit/ui/styles'));
|
|
243
|
+
if (!has) {
|
|
244
|
+
options.styles.unshift(exports.MK_STYLE_PATH);
|
|
245
|
+
changed = true;
|
|
246
|
+
add(findings, 'styles:mk-kit', `added ${exports.MK_STYLE_PATH}`, 'rewrite', '/theming');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return { text: changed ? JSON.stringify(json, null, 2) + '\n' : source, changed, findings };
|
|
250
|
+
}
|
|
251
|
+
/** Drop PrimeNG packages from dependencies and make sure @mk-kit/ui is present. */
|
|
252
|
+
function transformPackageJson(source, packages, mkVersion = '^0.38.0') {
|
|
253
|
+
const findings = [];
|
|
254
|
+
let json;
|
|
255
|
+
try {
|
|
256
|
+
json = JSON.parse(source);
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
return { text: source, changed: false, findings };
|
|
260
|
+
}
|
|
261
|
+
let changed = false;
|
|
262
|
+
for (const field of ['dependencies', 'devDependencies']) {
|
|
263
|
+
const deps = json[field];
|
|
264
|
+
if (!deps)
|
|
265
|
+
continue;
|
|
266
|
+
for (const p of packages) {
|
|
267
|
+
if (p in deps) {
|
|
268
|
+
delete deps[p];
|
|
269
|
+
changed = true;
|
|
270
|
+
add(findings, 'package:remove', `removed ${p} from ${field}`, 'rewrite');
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
json.dependencies ??= {};
|
|
275
|
+
if (!json.dependencies['@mk-kit/ui']) {
|
|
276
|
+
json.dependencies['@mk-kit/ui'] = mkVersion;
|
|
277
|
+
changed = true;
|
|
278
|
+
add(findings, 'package:add', `added @mk-kit/ui ${mkVersion}`, 'rewrite');
|
|
279
|
+
}
|
|
280
|
+
return { text: changed ? JSON.stringify(json, null, 2) + '\n' : source, changed, findings };
|
|
281
|
+
}
|
|
282
|
+
function add(findings, rule, message, kind, docs, count = 1) {
|
|
283
|
+
const existing = findings.find((f) => f.rule === rule && f.message === message);
|
|
284
|
+
if (existing)
|
|
285
|
+
existing.count += count;
|
|
286
|
+
else
|
|
287
|
+
findings.push({ rule, message, kind, docs: docsUrl(docs), count });
|
|
288
|
+
}
|
|
289
|
+
/** Markdown report of a whole run. */
|
|
290
|
+
function renderReport(files, options) {
|
|
291
|
+
const all = files.flatMap((f) => f.findings.map((x) => ({ ...x, file: f.path })));
|
|
292
|
+
const changed = files.filter((f) => f.changed);
|
|
293
|
+
const byKind = (kind) => all.filter((f) => f.kind === kind);
|
|
294
|
+
const sum = (list) => list.reduce((n, f) => n + f.count, 0);
|
|
295
|
+
const lines = [];
|
|
296
|
+
lines.push('# PrimeNG → mk-kit migration report', '');
|
|
297
|
+
lines.push(`${options.dryRun ? '**Dry run** — nothing was written. ' : ''}Scanned ${options.scanned} files, ${options.dryRun ? 'would change' : 'changed'} ${changed.length}.`, '');
|
|
298
|
+
lines.push(`- Automatic rewrites: **${sum(byKind('rewrite'))}**`);
|
|
299
|
+
lines.push(`- Needs a manual touch: **${sum(byKind('manual'))}**`);
|
|
300
|
+
lines.push(`- No mk-kit equivalent: **${sum(byKind('unmapped'))}**`, '');
|
|
301
|
+
lines.push('## Rewrites', '');
|
|
302
|
+
const rewrites = group(byKind('rewrite'));
|
|
303
|
+
lines.push(...(rewrites.length ? rewrites.map(([msg, n]) => `- ${msg} ×${n}`) : ['- (none)']), '');
|
|
304
|
+
lines.push('## Manual steps', '', 'Look for `<!-- mk-kit: … -->` and `// mk-kit:` comments in the changed files.', '');
|
|
305
|
+
const manual = group(byKind('manual'), true);
|
|
306
|
+
lines.push(...(manual.length ? manual.map(([msg, n, docs]) => `- ${msg} ×${n}${docs ? ` — [docs](${docs})` : ''}`) : ['- (none)']), '');
|
|
307
|
+
lines.push('## Not available in mk-kit', '');
|
|
308
|
+
const unmapped = group(byKind('unmapped'), true);
|
|
309
|
+
lines.push(...(unmapped.length ? unmapped.map(([msg, n, docs]) => `- ${msg} ×${n}${docs ? ` — closest: [docs](${docs})` : ''}`) : ['- (none)']), '');
|
|
310
|
+
lines.push('## Files', '');
|
|
311
|
+
lines.push(...(changed.length ? changed.map((f) => `- \`${f.path}\` — ${f.findings.map((x) => `${x.rule}×${x.count}`).join(', ')}`) : ['- (none)']), '');
|
|
312
|
+
lines.push('## Next', '');
|
|
313
|
+
lines.push('1. Run `ng build` and fix the remaining template errors — most are the `<!-- mk-kit -->` notes above.', '2. Replace `MessageService.add(...)` calls with `MkToastService` and `ConfirmationService.confirm(...)` with `await MkDialogService.confirm(...)`.', '3. Icons: `pi pi-*` classes → `<mk-icon name="…" />` (see https://mk-kit.dev/components/icon).', `4. Theme: \`--mk-*\` tokens replace the PrimeNG preset — https://mk-kit.dev/theming.`, '');
|
|
314
|
+
return lines.join('\n');
|
|
315
|
+
}
|
|
316
|
+
function group(list, withDocs = false) {
|
|
317
|
+
const map = new Map();
|
|
318
|
+
for (const f of list) {
|
|
319
|
+
const key = f.message;
|
|
320
|
+
const cur = map.get(key) ?? [0, f.docs];
|
|
321
|
+
cur[0] += f.count;
|
|
322
|
+
map.set(key, cur);
|
|
323
|
+
}
|
|
324
|
+
return [...map.entries()].map(([msg, [n, docs]]) => [msg, n, withDocs ? docs : undefined]);
|
|
325
|
+
}
|