Skip to content

Commit 0233f8e

Browse files
authored
fix(dts-generator): fix more derived type names for module:* classes (#523)
When a managed object class has a module:* name, the names are now correctly split into prefix and base name when calculating the name of the EventParameters and EventType types. The implementation of the calculations is now shared with the calculation implemented in b6c0fe5.
1 parent 30351a8 commit 0233f8e

File tree

4 files changed

+108
-102
lines changed

4 files changed

+108
-102
lines changed

packages/dts-generator/src/phases/json-fixer.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
import { TypeParser } from "../utils/type-parser.js";
2121
import { TSASTTypeBuilder } from "../utils/ts-ast-type-builder.js";
2222
import { addConstructorSettingsInterfaces } from "../utils/json-constructor-settings-interfaces.js";
23-
import { splitName } from "../utils/base-utils.js";
23+
import { calculateDerivedNames, splitName } from "../utils/base-utils.js";
2424
import { Directives } from "../generate-from-objects.js";
2525
import { addEventParameterInterfaces } from "../utils/json-event-parameter-interfaces.js";
2626

@@ -452,12 +452,7 @@ function determineMissingExportsForTypes(symbols: ConcreteSymbol[]) {
452452

453453
function basename(fqn: string) {
454454
// Note: this differs from splitName(fqn)[0] as it handles module:* namepaths
455-
if (fqn.startsWith("module:")) {
456-
const p = fqn.lastIndexOf("/");
457-
fqn = fqn.slice(p + 1);
458-
}
459-
const p = fqn.lastIndexOf(".");
460-
return fqn.slice(p + 1);
455+
return calculateDerivedNames(fqn).basename;
461456
}
462457

463458
symbols.forEach((symbol) => {
@@ -481,12 +476,13 @@ function determineMissingExportsForTypes(symbols: ConcreteSymbol[]) {
481476
} else {
482477
// non-canonical names need to be reported at least
483478
// TODO consumers need some documentation about the necessary imports (SDK enhancement?)
479+
const { exportName } = calculateDerivedNames(symbol.name);
484480
log.warn(
485481
`**** symbol ${symbol.name} exported from "${
486482
symbol.module
487-
}" as "${basename(symbol.name)}"`,
483+
}" as "${exportName}"`,
488484
);
489-
symbol.export = basename(symbol.name);
485+
symbol.export = exportName;
490486
}
491487
});
492488
}

packages/dts-generator/src/utils/base-utils.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,82 @@ export function splitName(name: string, separator = ".") {
22
const p = name.lastIndexOf(separator);
33
return p < 0 ? ["", name] : [name.slice(0, p), name.slice(p + 1)];
44
}
5+
6+
/*
7+
* Calculates a derived name from an entity name by first calculating the basename
8+
* of the entity, then applying the given `createDerivedBasename` transformation and
9+
* then building a qualified name again from it.
10+
*
11+
* The returned object contains the fully qualified name `name`, the derived `basename`
12+
* and the assumed `exportName` under which the derived entity is exported from the
13+
* containing module.
14+
*
15+
* Examples (based on the $<Entity>Settings transformation):
16+
* (1) sap.m.Button -->
17+
* name: "sap.m.$ButtonSettings"
18+
* basename: "$ButtonSettings"
19+
* exportName: "$ButtonSettings"
20+
*
21+
* (2) module:my/lib//NotificationListGroupItem -->
22+
* name: "module:my/lib/NotificationListGroupItem.$NotificationListGroupItemSettings"
23+
* basename: "$NotificationListGroupItemSettings"
24+
* exportName: "$NotificationListGroupItemSettings"
25+
*
26+
* (3) module:my/lib/SegmentedButton.Item
27+
* name: "module:my/lib/SegmentedButton.$ItemSettings"
28+
* basename: "$ItemSettings"
29+
* exportName: "$ItemSettings"
30+
*
31+
* (4) module:my/lib/SegmentedButton.Item.SubItem
32+
* name: "module:my/lib/SegmentedButton.Item.$SubItemSettings"
33+
* basename: "$SubItemSettings"
34+
* exportName: "Item.$SubItemSettings"
35+
*/
36+
export function calculateDerivedNames(
37+
fqn: string,
38+
createDerivedBasename: (basename: string) => string = (basename) => basename,
39+
): {
40+
name: string;
41+
basename: string;
42+
exportName: string;
43+
} {
44+
const makeNameAndBasename = (fqn: string) => {
45+
const [prefix, basename] = splitName(fqn);
46+
const derivedBasename = createDerivedBasename(basename);
47+
return [`${prefix}${prefix ? "." : ""}${derivedBasename}`, derivedBasename];
48+
};
49+
50+
if (fqn.startsWith("module:")) {
51+
const [pkgname, moduleAndExport] = splitName(
52+
fqn.slice("module:".length),
53+
"/",
54+
);
55+
const pos = moduleAndExport.indexOf(".");
56+
if (pos < 0) {
57+
// case (2), default export
58+
const [, settingsName] = makeNameAndBasename(moduleAndExport);
59+
return {
60+
name: `${fqn}.${settingsName}`,
61+
basename: settingsName,
62+
exportName: settingsName,
63+
};
64+
}
65+
// case (3) and (4), named export
66+
const moduleBaseName = moduleAndExport.slice(0, pos);
67+
const exportName = moduleAndExport.slice(pos + 1);
68+
const [settingsNameWithPrefix, settingsName] =
69+
makeNameAndBasename(exportName);
70+
return {
71+
name: `module:${pkgname}${pkgname ? "/" : ""}${moduleBaseName}.${settingsNameWithPrefix}`,
72+
basename: settingsName,
73+
exportName: settingsNameWithPrefix,
74+
};
75+
}
76+
// case (1), global name
77+
const [fqSettingsName, settingsName] = makeNameAndBasename(fqn);
78+
return {
79+
name: fqSettingsName,
80+
basename: settingsName,
81+
exportName: settingsName,
82+
};
83+
}

packages/dts-generator/src/utils/json-constructor-settings-interfaces.ts

Lines changed: 2 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -4,80 +4,13 @@ import {
44
ClassSymbol,
55
ConcreteSymbol,
66
InterfaceSymbol,
7-
ObjConstructor,
8-
ObjEvent,
9-
Ui5Property,
107
} from "../types/api-json.js";
118
const log = getLogger("@ui5/dts-generator/constructor-settings-interfaces");
12-
import { splitName } from "./base-utils.js";
9+
import { calculateDerivedNames } from "./base-utils.js";
1310
import { TypeReference } from "../types/ast.js";
1411

15-
/*
16-
* Calculates the name for the $Settings interface, the basename of the name
17-
* (last name segment) and the assumed export name under which the settings
18-
* are exported from the containing module.
19-
*
20-
* Examples:
21-
* (1) sap.m.Button -->
22-
* name: "sap.m.$ButtonSettings"
23-
* basename: "$ButtonSettings"
24-
* exportName: "$ButtonSettings"
25-
*
26-
* (2) module:my/lib//NotificationListGroupItem -->
27-
* name: "module:my/lib/NotificationListGroupItem.$NotificationListGroupItemSettings"
28-
* basename: "$NotificationListGroupItemSettings"
29-
* exportName: "$NotificationListGroupItemSettings"
30-
*
31-
* (3) module:my/lib/SegmentedButton.Item
32-
* name: "module:my/lib/SegmentedButton.$ItemSettings"
33-
* basename: "$ItemSettings"
34-
* exportName: "$ItemSettings"
35-
*
36-
* (4) module:my/lib/SegmentedButton.Item.SubItem
37-
* name: "module:my/lib/SegmentedButton.Item.$SubItemSettings"
38-
* basename: "$SubItemSettings"
39-
* exportName: "Item.$SubItemSettings"
40-
*/
4112
export function makeSettingsNames(fqn: string) {
42-
const makeNameAndBasename = (fqn: string) => {
43-
const [prefix, basename] = splitName(fqn);
44-
const settings = `\$${basename}Settings`;
45-
return [`${prefix}${prefix ? "." : ""}${settings}`, settings];
46-
};
47-
48-
if (fqn.startsWith("module:")) {
49-
const [pkgname, moduleAndExport] = splitName(
50-
fqn.slice("module:".length),
51-
"/",
52-
);
53-
const pos = moduleAndExport.indexOf(".");
54-
if (pos < 0) {
55-
// case (2), default export
56-
const [, settingsName] = makeNameAndBasename(moduleAndExport);
57-
return {
58-
name: `${fqn}.${settingsName}`,
59-
basename: settingsName,
60-
exportName: settingsName,
61-
};
62-
}
63-
// case (3) and (4), named export
64-
const moduleBaseName = moduleAndExport.slice(0, pos);
65-
const exportName = moduleAndExport.slice(pos + 1);
66-
const [settingsNameWithPrefix, settingsName] =
67-
makeNameAndBasename(exportName);
68-
return {
69-
name: `module:${pkgname}${pkgname ? "/" : ""}${moduleBaseName}.${settingsNameWithPrefix}`,
70-
basename: settingsName,
71-
exportName: settingsNameWithPrefix,
72-
};
73-
}
74-
// case (1), global name
75-
const [fqSettingsName, settingsName] = makeNameAndBasename(fqn);
76-
return {
77-
name: fqSettingsName,
78-
basename: settingsName,
79-
exportName: settingsName,
80-
};
13+
return calculateDerivedNames(fqn, (basename) => `\$${basename}Settings`);
8114
}
8215

8316
export function isA(

packages/dts-generator/src/utils/json-event-parameter-interfaces.ts

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@ import {
55
ConcreteSymbol,
66
InterfaceSymbol,
77
ObjCallableParameter,
8-
ObjEvent,
9-
ObjMethod,
108
TypedefSymbol,
119
Ui5Event,
1210
} from "../types/api-json.js";
1311
const log = getLogger("@ui5/dts-generator/constructor-settings-interfaces");
14-
import { splitName } from "./base-utils.js";
12+
import { calculateDerivedNames } from "./base-utils.js";
1513
import {
1614
addJsDocProps,
1715
isA,
@@ -60,12 +58,16 @@ function createEventParameterInterfaces(
6058
const makeEventParametersName = (fqn: string, eventName: string) => {
6159
const capitalizedEventName =
6260
eventName.charAt(0).toUpperCase() + eventName.slice(1);
63-
const [pkgname, basename] = splitName(fqn);
6461
return {
65-
fullEventParametersName: `${pkgname}.${basename}\$${capitalizedEventName}EventParameters`,
66-
shortEventParametersName: `${basename}\$${capitalizedEventName}EventParameters`,
67-
fullEventTypealiasName: `${pkgname}.${basename}\$${capitalizedEventName}Event`,
68-
shortEventTypealiasName: `${basename}\$${capitalizedEventName}Event`,
62+
eventParametersNames: calculateDerivedNames(
63+
fqn,
64+
(basename: string) =>
65+
`${basename}\$${capitalizedEventName}EventParameters`,
66+
),
67+
eventTypeAliasNames: calculateDerivedNames(
68+
fqn,
69+
(basename: string) => `${basename}\$${capitalizedEventName}Event`,
70+
),
6971
};
7072
};
7173

@@ -254,19 +256,15 @@ function createEventParameterInterfaces(
254256
if (symbol.events) {
255257
symbol.events.forEach((event) => {
256258
// build the interface (with no properties (event parameters) yet)
257-
const {
258-
fullEventParametersName,
259-
shortEventParametersName,
260-
fullEventTypealiasName,
261-
shortEventTypealiasName,
262-
} = makeEventParametersName(symbol.name, event.name);
259+
const { eventParametersNames, eventTypeAliasNames } =
260+
makeEventParametersName(symbol.name, event.name);
263261
const eventParametersInterface: InterfaceSymbol = {
264262
kind: "interface",
265-
name: fullEventParametersName,
266-
basename: shortEventParametersName,
263+
name: eventParametersNames.name,
264+
basename: eventParametersNames.basename,
267265
module: symbol.module,
268266
resource: symbol.resource,
269-
export: shortEventParametersName,
267+
export: eventParametersNames.exportName,
270268
visibility: symbol.visibility,
271269
__isNotAMarkerInterface: true,
272270
};
@@ -290,7 +288,7 @@ function createEventParameterInterfaces(
290288
eventParametersInterface.extends = makeEventParametersName(
291289
superClass.name,
292290
superEvent.name,
293-
).fullEventParametersName;
291+
).eventParametersNames.name;
294292
}
295293

296294
// get inherited parameters to check compatibility and remove them from the new inheriting interface
@@ -338,25 +336,25 @@ function createEventParameterInterfaces(
338336
// now also create a type alias for the event with these parameters and the concrete event source type
339337
const eventTypedef: TypedefSymbol = {
340338
kind: "typedef",
341-
name: fullEventTypealiasName,
339+
name: eventTypeAliasNames.name,
342340
type: {
343341
kind: "TypeReference",
344342
typeName: "sap.ui.base.Event",
345343
typeArguments: [
346344
{
347345
kind: "TypeReference",
348-
typeName: shortEventParametersName,
346+
typeName: eventParametersNames.basename,
349347
},
350348
{
351349
kind: "TypeReference",
352350
typeName: symbol.basename,
353351
},
354352
],
355353
},
356-
basename: shortEventTypealiasName,
354+
basename: eventTypeAliasNames.basename,
357355
module: symbol.module,
358356
resource: symbol.resource,
359-
export: shortEventTypealiasName,
357+
export: eventTypeAliasNames.exportName,
360358
visibility: symbol.visibility,
361359
};
362360
eventTypedef.description = `Event object of the ${symbol.basename}#${event.name} event.`;
@@ -374,7 +372,7 @@ function createEventParameterInterfaces(
374372
enrichEventMethods(
375373
symbol,
376374
event.name,
377-
shortEventTypealiasName,
375+
eventTypeAliasNames.basename,
378376
eventParametersInterface.name,
379377
);
380378

@@ -399,7 +397,7 @@ function createEventParameterInterfaces(
399397
"sap.ui.base.Event"
400398
) {
401399
eventPropertyType.parameters[0].type.typeName =
402-
shortEventTypealiasName; // replace "Event" with the specific event type
400+
eventTypeAliasNames.basename; // replace "Event" with the specific event type
403401
} else {
404402
log.info(
405403
`Property '${event.name}' in the settings object for ${symbol.name} does not relate to an event. Maybe shadowed by a property with same name.`,

0 commit comments

Comments
 (0)