Skip to content

Commit 8d9ffd4

Browse files
authored
Angular: Configuration Components Deprecation Warning (#31511)
1 parent ef02f1d commit 8d9ffd4

File tree

7 files changed

+1223
-0
lines changed

7 files changed

+1223
-0
lines changed

apps/demos/testing/known-warnings.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export const knownWarnings = [
22
'W0019 -',
33
'W0022 -',
44
'W2108 -',
5+
'W3001 -',
56
];

packages/devextreme-angular/src/core/deprecated-config-map.ts

Lines changed: 1090 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import type { BaseNestedOption, INestedOptionContainer } from './nested-option';
2+
import { DEPRECATED_CONFIG_COMPONENTS } from './deprecated-config-map';
3+
import WARNING_CODES from './warning-codes';
4+
import { logWarning } from './warning-helper';
5+
6+
const warnedUsages = new Set<string>();
7+
8+
const NESTED_CLASS_NAME_REGEXP = /^(Dx[io][A-Z]\w+)Component$/;
9+
10+
type DeprecatedConfigEntry = Record<string, string>;
11+
12+
function getLegacySelector(nestedOption: BaseNestedOption): string | undefined {
13+
const className = nestedOption?.constructor?.name;
14+
if (!className) {
15+
return undefined;
16+
}
17+
18+
const match = NESTED_CLASS_NAME_REGEXP.exec(className);
19+
if (!match) {
20+
return undefined;
21+
}
22+
23+
const [, legacyName] = match;
24+
25+
return toKebabCase(legacyName);
26+
}
27+
28+
function getHostMapping(host: INestedOptionContainer | undefined): DeprecatedConfigEntry | undefined {
29+
const visited = new Set<INestedOptionContainer>();
30+
let current = host;
31+
32+
while (current && !visited.has(current)) {
33+
visited.add(current);
34+
35+
const ctorName = current.constructor?.name;
36+
if (ctorName && Object.prototype.hasOwnProperty.call(DEPRECATED_CONFIG_COMPONENTS, ctorName)) {
37+
return DEPRECATED_CONFIG_COMPONENTS[ctorName] as DeprecatedConfigEntry;
38+
}
39+
40+
current = (current as { _host?: INestedOptionContainer })._host;
41+
}
42+
43+
return undefined;
44+
}
45+
46+
export function warnIfLegacyNestedComponent(nestedOption: BaseNestedOption, host: INestedOptionContainer | undefined): void {
47+
const legacySelector = getLegacySelector(nestedOption);
48+
if (!legacySelector) {
49+
return;
50+
}
51+
52+
const mappingEntry = getHostMapping(host);
53+
if (!mappingEntry) {
54+
return;
55+
}
56+
57+
const replacement = mappingEntry[legacySelector];
58+
if (!replacement) {
59+
return;
60+
}
61+
62+
const cacheKey = `${legacySelector}|${replacement}`;
63+
if (warnedUsages.has(cacheKey)) {
64+
return;
65+
}
66+
67+
warnedUsages.add(cacheKey);
68+
69+
logWarning(
70+
WARNING_CODES.LEGACY_CONFIG_COMPONENT_USED,
71+
{ legacySelector, replacement },
72+
);
73+
}
74+
75+
function toKebabCase(value: string): string {
76+
return value
77+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
78+
.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
79+
.toLowerCase();
80+
}

packages/devextreme-angular/src/core/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ export * from './template';
88
export * from './transfer-state';
99
export * from './utils';
1010
export * from './watcher-helper';
11+
export * from './warning-helper';
12+
export * from './warning-codes';

packages/devextreme-angular/src/core/nested-option.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import render from 'devextreme/core/renderer';
66
import { triggerHandler } from 'devextreme/events';
77
import domAdapter from 'devextreme/core/dom_adapter';
88
import { getElement } from './utils';
9+
import { warnIfLegacyNestedComponent } from './deprecated-config-warning';
910
import { DX_TEMPLATE_WRAPPER_CLASS } from './template';
1011

1112
const VISIBILITY_CHANGE_SELECTOR = 'dx-visibility-change-handler';
@@ -292,5 +293,6 @@ export class NestedOptionHost {
292293

293294
setNestedOption(nestedOption: BaseNestedOption) {
294295
nestedOption.setHost(this._host, this._optionPath);
296+
warnIfLegacyNestedComponent(nestedOption, this._host);
295297
}
296298
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const WARNING_CODES = {
2+
LEGACY_CONFIG_COMPONENT_USED: {
3+
code: 'W3001',
4+
template: 'You are using the legacy {legacySelector} configuration component. We recommend '
5+
+ 'upgrading to the new {replacement} configuration component.',
6+
// TODO: link to docs article, when available
7+
},
8+
} as const;
9+
10+
export type WarningId = keyof typeof WARNING_CODES;
11+
export type WarningDefinition = typeof WARNING_CODES[WarningId];
12+
13+
export default WARNING_CODES;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type { WarningDefinition } from './warning-codes';
2+
3+
type TemplatePrimitive = string | number | boolean;
4+
export type TemplateArgs = TemplatePrimitive[] | Record<string, TemplatePrimitive>;
5+
6+
function formatWarningMessage(template: string, args?: TemplateArgs): string {
7+
if (!args) {
8+
return template;
9+
}
10+
11+
if (Array.isArray(args)) {
12+
return args.reduce<string>(
13+
(message, value, index) => replacePlaceholder(message, `{${index}}`, String(value)),
14+
template,
15+
);
16+
}
17+
18+
return Object.entries(args).reduce<string>(
19+
(message, [key, value]) => replacePlaceholder(message, `{${key}}`, String(value)),
20+
template,
21+
);
22+
}
23+
24+
export function logWarning(warning: WarningDefinition, args?: TemplateArgs): void {
25+
if (typeof console === 'undefined' || typeof console.warn !== 'function') {
26+
return;
27+
}
28+
29+
const message = formatWarningMessage(warning.template, args);
30+
console.warn(`${warning.code} - ${message}`);
31+
}
32+
33+
function replacePlaceholder(message: string, placeholder: string, value: string): string {
34+
return message.split(placeholder).join(value);
35+
}

0 commit comments

Comments
 (0)