Skip to content

Commit 3087912

Browse files
authored
Resolve potential name conflicts of type names and generated component names (#824)
1 parent 968a2ef commit 3087912

File tree

4 files changed

+105
-14
lines changed

4 files changed

+105
-14
lines changed

packages/devextreme-react-generator/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"author": "Developer Express Inc.",
33
"name": "devextreme-react-generator",
4-
"version": "4.0.1",
4+
"version": "4.0.2",
55
"description": "DevExtreme React UI and Visualization Components",
66
"repository": {
77
"type": "git",

packages/devextreme-react-generator/src/generator.test.ts

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,18 @@ describe('mapWidget', () => {
773773
}],
774774
props: [],
775775
firedEvents: [],
776+
},
777+
{
778+
name: 'optionThatConflictsWithComponent',
779+
isSubscribable: true,
780+
isDeprecated: false,
781+
types: [{
782+
type: 'ComplexOption1',
783+
acceptableValues: [],
784+
isCustomType: true,
785+
}],
786+
props: [],
787+
firedEvents: [],
776788
}],
777789
};
778790

@@ -817,6 +829,13 @@ describe('mapWidget', () => {
817829
templates: [],
818830
types: [],
819831
module: '',
832+
},
833+
{
834+
name: 'ComplexOption1',
835+
props: [],
836+
templates: [],
837+
types: [],
838+
module: 'complex/option/module',
820839
}];
821840

822841
it('should process subscribable options', () => {
@@ -881,6 +900,47 @@ describe('mapWidget', () => {
881900
expect(component.nestedComponents?.[1].className).toBe('ComplexOption2');
882901
expect(component.nestedComponents?.[1].optionName).toBe('widgetComplexOption2');
883902
});
903+
it('should resolve name conflicts in complex options', () => {
904+
const { component, customTypeImports } = mapWidget(
905+
{ ...widgetWithCustomTypes, complexOptions },
906+
'',
907+
'',
908+
'',
909+
customTypesWithModules,
910+
'',
911+
{
912+
generateCustomTypes: true,
913+
},
914+
);
915+
expect(component.nestedComponents?.length).toBe(2);
916+
expect(component.nestedComponents?.[0].className).toBe('ComplexOption1');
917+
expect(customTypeImports!['devextreme/complex/option/module']).toEqual(['ComplexOption1 as ComplexOption1Aliased']);
918+
});
919+
920+
it('should resolve name conflicts in complex options with overrides if present', () => {
921+
const importOverridesMetadata: ImportOverridesMetadata = {
922+
nameConflictsResolutionNamespaces: {
923+
ComplexOption1: 'NamespaceForNestedComponentNamesConflict',
924+
},
925+
};
926+
927+
const { component, customTypeImports, wildcardTypeImports } = mapWidget(
928+
{ ...widgetWithCustomTypes, complexOptions },
929+
'',
930+
'',
931+
'',
932+
customTypesWithModules,
933+
'',
934+
{
935+
generateCustomTypes: true,
936+
importOverridesMetadata,
937+
},
938+
);
939+
expect(component.nestedComponents?.length).toBe(2);
940+
expect(component.nestedComponents?.[0].className).toBe('ComplexOption1');
941+
expect(customTypeImports!['devextreme/complex/option/module']).toBeUndefined();
942+
expect(wildcardTypeImports).toEqual({ 'devextreme/complex/option/module': 'NamespaceForNestedComponentNamesConflict' });
943+
});
884944

885945
it('should process custom types', () => {
886946
const { component } = mapWidget(
@@ -945,7 +1005,6 @@ describe('mapWidget', () => {
9451005
expect(resultOptions.option4.type).toEqual('NoConflictNamespace.CustomTypeWithNameConflict');
9461006
expect(resultOptions.option5.type).toEqual('CustomTypeWithDefaultImport');
9471007
expect(resultOptions.option6.type).toEqual('CustomGenericType<any>');
948-
9491008
expect(customTypeImports!['devextreme/custom/type/module']).toEqual(['CustomTypeWithModule']);
9501009
expect(customTypeImports!['overridden/module']).toEqual(['CustomTypeWithoutModule']);
9511010
expect(defaultTypeImports).toEqual({ CustomTypeWithDefaultImport: 'module/with/default/import' });

packages/devextreme-react-generator/src/generator.ts

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,18 @@ export function convertToBaseType(type: string): BaseTypes {
8989
}
9090

9191
export function createCustomTypeResolver(
92-
importOverridesMetadata: ImportOverridesMetadata, widgetCustomTypesCollector: Set<string>,
92+
importOverridesMetadata: ImportOverridesMetadata,
93+
widgetCustomTypesCollector: Set<string>,
94+
resolveNameConflicts: (string) => string = (typeName) => typeName,
9395
): TypeResolver {
9496
return (typeDescriptor: ITypeDescr) => {
9597
const resolvedType = importOverridesMetadata.typeResolutions?.[typeDescriptor.type]
9698
|| typeDescriptor.type;
9799
widgetCustomTypesCollector.add(resolvedType);
98-
const resultingType = importOverridesMetadata.nameConflictsResolutionNamespaces?.[resolvedType]
99-
? `${importOverridesMetadata.nameConflictsResolutionNamespaces[resolvedType]}.${resolvedType}` : resolvedType;
100+
const resultingType = resolveNameConflicts(
101+
importOverridesMetadata.nameConflictsResolutionNamespaces?.[resolvedType]
102+
? `${importOverridesMetadata.nameConflictsResolutionNamespaces[resolvedType]}.${resolvedType}` : resolvedType,
103+
);
100104
return importOverridesMetadata.genericTypes?.[resultingType] ? `${resultingType}<any>` : resultingType;
101105
};
102106
}
@@ -191,18 +195,22 @@ export function mapOption(prop: IProp, typeResolver?: TypeResolver): IOption {
191195
};
192196
}
193197

194-
export function extractNestedComponents(
195-
props: IComplexProp[],
196-
rawWidgetName: string,
197-
widgetName: string,
198-
typeResolver?: TypeResolver,
199-
): INestedComponent[] {
198+
function getWidgetComponentNames(rawWidgetName: string, widgetName: string, props: IComplexProp[]) {
200199
const nameClassMap: Record<string, string> = {};
201200
nameClassMap[rawWidgetName] = widgetName;
202201
props.forEach((p) => {
203202
nameClassMap[p.name] = uppercaseFirst(p.name);
204203
});
204+
return nameClassMap;
205+
}
205206

207+
export function extractNestedComponents(
208+
props: IComplexProp[],
209+
rawWidgetName: string,
210+
widgetName: string,
211+
typeResolver?: TypeResolver,
212+
): INestedComponent[] {
213+
const nameClassMap = getWidgetComponentNames(rawWidgetName, widgetName, props);
206214
return props.map((p) => ({
207215
className: nameClassMap[p.name],
208216
owners: p.owners.map((o) => nameClassMap[o]),
@@ -305,8 +313,29 @@ export function mapWidget(
305313
const widgetCustomTypes = new Set<string>();
306314
const { importOverridesMetadata, generateCustomTypes } = typeGenerationOptions || {};
307315

316+
const generatedComponentNames = Object.values(
317+
getWidgetComponentNames(raw.name, name, raw.complexOptions || []),
318+
);
319+
320+
const typeAliases: Record<string, string> = {};
321+
const resolveGeneratedComponentNamesConflict = (typeName: string) => {
322+
if (generatedComponentNames.includes(typeName)) {
323+
const aliasedTypeName = `${typeName}Aliased`;
324+
typeAliases[typeName] = aliasedTypeName;
325+
return aliasedTypeName;
326+
}
327+
return typeName;
328+
};
329+
const getTypeImportStatement = (typeName: string) => (
330+
typeAliases[typeName] ? `${typeName} as ${typeAliases[typeName]}` : typeName
331+
);
332+
308333
const typeResolver = generateCustomTypes
309-
? createCustomTypeResolver(importOverridesMetadata || {}, widgetCustomTypes) : undefined;
334+
? createCustomTypeResolver(
335+
importOverridesMetadata || {},
336+
widgetCustomTypes,
337+
resolveGeneratedComponentNamesConflict,
338+
) : undefined;
310339

311340
const subscribableOptions: ISubscribableOption[] = collectSubscribableRecursively(raw.options)
312341
.map((option) => mapSubscribableOption(option, typeResolver));
@@ -342,7 +371,10 @@ export function mapWidget(
342371
if (moduleImportNamespace) {
343372
wildcardTypeImports[module] = moduleImportNamespace;
344373
} else {
345-
customTypeImports[module] = [...(customTypeImports[module] || []), t];
374+
customTypeImports[module] = [
375+
...(customTypeImports[module] || []),
376+
getTypeImportStatement(t),
377+
];
346378
}
347379
}
348380
});

packages/devextreme-react/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"@types/react-dom": "^16.9.16",
3636
"del": "^3.0.0",
3737
"devextreme": "23.1-next",
38-
"devextreme-react-generator": "^4.0.1",
38+
"devextreme-react-generator": "^4.0.2",
3939
"gulp": "^4.0.2",
4040
"jest": "^25.0.0",
4141
"react": "~18.0.0",

0 commit comments

Comments
 (0)