Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions packages/dts-generator/src/phases/json-fixer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import { TypeParser } from "../utils/type-parser.js";
import { TSASTTypeBuilder } from "../utils/ts-ast-type-builder.js";
import { addConstructorSettingsInterfaces } from "../utils/json-constructor-settings-interfaces.js";
import { splitName } from "../utils/base-utils.js";
import { calculateDerivedNames, splitName } from "../utils/base-utils.js";
import { Directives } from "../generate-from-objects.js";
import { addEventParameterInterfaces } from "../utils/json-event-parameter-interfaces.js";

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

function basename(fqn: string) {
// Note: this differs from splitName(fqn)[0] as it handles module:* namepaths
if (fqn.startsWith("module:")) {
const p = fqn.lastIndexOf("/");
fqn = fqn.slice(p + 1);
}
const p = fqn.lastIndexOf(".");
return fqn.slice(p + 1);
return calculateDerivedNames(fqn).basename;
}

symbols.forEach((symbol) => {
Expand All @@ -481,12 +476,13 @@ function determineMissingExportsForTypes(symbols: ConcreteSymbol[]) {
} else {
// non-canonical names need to be reported at least
// TODO consumers need some documentation about the necessary imports (SDK enhancement?)
const { exportName } = calculateDerivedNames(symbol.name);
log.warn(
`**** symbol ${symbol.name} exported from "${
symbol.module
}" as "${basename(symbol.name)}"`,
}" as "${exportName}"`,
);
symbol.export = basename(symbol.name);
symbol.export = exportName;
}
});
}
Expand Down
79 changes: 79 additions & 0 deletions packages/dts-generator/src/utils/base-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,82 @@ export function splitName(name: string, separator = ".") {
const p = name.lastIndexOf(separator);
return p < 0 ? ["", name] : [name.slice(0, p), name.slice(p + 1)];
}

/*
* Calculates a derived name from an entity name by first calculating the basename
* of the entity, then applying the given `createDerivedBasename` transformation and
* then building a qualified name again from it.
*
* The returned object contains the fully qualified name `name`, the derived `basename`
* and the assumed `exportName` under which the derived entity is exported from the
* containing module.
*
* Examples (based on the $<Entity>Settings transformation):
* (1) sap.m.Button -->
* name: "sap.m.$ButtonSettings"
* basename: "$ButtonSettings"
* exportName: "$ButtonSettings"
*
* (2) module:my/lib//NotificationListGroupItem -->
* name: "module:my/lib/NotificationListGroupItem.$NotificationListGroupItemSettings"
* basename: "$NotificationListGroupItemSettings"
* exportName: "$NotificationListGroupItemSettings"
*
* (3) module:my/lib/SegmentedButton.Item
* name: "module:my/lib/SegmentedButton.$ItemSettings"
* basename: "$ItemSettings"
* exportName: "$ItemSettings"
*
* (4) module:my/lib/SegmentedButton.Item.SubItem
* name: "module:my/lib/SegmentedButton.Item.$SubItemSettings"
* basename: "$SubItemSettings"
* exportName: "Item.$SubItemSettings"
*/
export function calculateDerivedNames(
fqn: string,
createDerivedBasename: (basename: string) => string = (basename) => basename,
): {
name: string;
basename: string;
exportName: string;
} {
const makeNameAndBasename = (fqn: string) => {
const [prefix, basename] = splitName(fqn);
const derivedBasename = createDerivedBasename(basename);
return [`${prefix}${prefix ? "." : ""}${derivedBasename}`, derivedBasename];
};

if (fqn.startsWith("module:")) {
const [pkgname, moduleAndExport] = splitName(
fqn.slice("module:".length),
"/",
);
const pos = moduleAndExport.indexOf(".");
if (pos < 0) {
// case (2), default export
const [, settingsName] = makeNameAndBasename(moduleAndExport);
return {
name: `${fqn}.${settingsName}`,
basename: settingsName,
exportName: settingsName,
};
}
// case (3) and (4), named export
const moduleBaseName = moduleAndExport.slice(0, pos);
const exportName = moduleAndExport.slice(pos + 1);
const [settingsNameWithPrefix, settingsName] =
makeNameAndBasename(exportName);
return {
name: `module:${pkgname}${pkgname ? "/" : ""}${moduleBaseName}.${settingsNameWithPrefix}`,
basename: settingsName,
exportName: settingsNameWithPrefix,
};
}
// case (1), global name
const [fqSettingsName, settingsName] = makeNameAndBasename(fqn);
return {
name: fqSettingsName,
basename: settingsName,
exportName: settingsName,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,80 +4,13 @@ import {
ClassSymbol,
ConcreteSymbol,
InterfaceSymbol,
ObjConstructor,
ObjEvent,
Ui5Property,
} from "../types/api-json.js";
const log = getLogger("@ui5/dts-generator/constructor-settings-interfaces");
import { splitName } from "./base-utils.js";
import { calculateDerivedNames } from "./base-utils.js";
import { TypeReference } from "../types/ast.js";

/*
* Calculates the name for the $Settings interface, the basename of the name
* (last name segment) and the assumed export name under which the settings
* are exported from the containing module.
*
* Examples:
* (1) sap.m.Button -->
* name: "sap.m.$ButtonSettings"
* basename: "$ButtonSettings"
* exportName: "$ButtonSettings"
*
* (2) module:my/lib//NotificationListGroupItem -->
* name: "module:my/lib/NotificationListGroupItem.$NotificationListGroupItemSettings"
* basename: "$NotificationListGroupItemSettings"
* exportName: "$NotificationListGroupItemSettings"
*
* (3) module:my/lib/SegmentedButton.Item
* name: "module:my/lib/SegmentedButton.$ItemSettings"
* basename: "$ItemSettings"
* exportName: "$ItemSettings"
*
* (4) module:my/lib/SegmentedButton.Item.SubItem
* name: "module:my/lib/SegmentedButton.Item.$SubItemSettings"
* basename: "$SubItemSettings"
* exportName: "Item.$SubItemSettings"
*/
export function makeSettingsNames(fqn: string) {
const makeNameAndBasename = (fqn: string) => {
const [prefix, basename] = splitName(fqn);
const settings = `\$${basename}Settings`;
return [`${prefix}${prefix ? "." : ""}${settings}`, settings];
};

if (fqn.startsWith("module:")) {
const [pkgname, moduleAndExport] = splitName(
fqn.slice("module:".length),
"/",
);
const pos = moduleAndExport.indexOf(".");
if (pos < 0) {
// case (2), default export
const [, settingsName] = makeNameAndBasename(moduleAndExport);
return {
name: `${fqn}.${settingsName}`,
basename: settingsName,
exportName: settingsName,
};
}
// case (3) and (4), named export
const moduleBaseName = moduleAndExport.slice(0, pos);
const exportName = moduleAndExport.slice(pos + 1);
const [settingsNameWithPrefix, settingsName] =
makeNameAndBasename(exportName);
return {
name: `module:${pkgname}${pkgname ? "/" : ""}${moduleBaseName}.${settingsNameWithPrefix}`,
basename: settingsName,
exportName: settingsNameWithPrefix,
};
}
// case (1), global name
const [fqSettingsName, settingsName] = makeNameAndBasename(fqn);
return {
name: fqSettingsName,
basename: settingsName,
exportName: settingsName,
};
return calculateDerivedNames(fqn, (basename) => `\$${basename}Settings`);
}

export function isA(
Expand Down
46 changes: 22 additions & 24 deletions packages/dts-generator/src/utils/json-event-parameter-interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ import {
ConcreteSymbol,
InterfaceSymbol,
ObjCallableParameter,
ObjEvent,
ObjMethod,
TypedefSymbol,
Ui5Event,
} from "../types/api-json.js";
const log = getLogger("@ui5/dts-generator/constructor-settings-interfaces");
import { splitName } from "./base-utils.js";
import { calculateDerivedNames } from "./base-utils.js";
import {
addJsDocProps,
isA,
Expand Down Expand Up @@ -60,12 +58,16 @@ function createEventParameterInterfaces(
const makeEventParametersName = (fqn: string, eventName: string) => {
const capitalizedEventName =
eventName.charAt(0).toUpperCase() + eventName.slice(1);
const [pkgname, basename] = splitName(fqn);
return {
fullEventParametersName: `${pkgname}.${basename}\$${capitalizedEventName}EventParameters`,
shortEventParametersName: `${basename}\$${capitalizedEventName}EventParameters`,
fullEventTypealiasName: `${pkgname}.${basename}\$${capitalizedEventName}Event`,
shortEventTypealiasName: `${basename}\$${capitalizedEventName}Event`,
eventParametersNames: calculateDerivedNames(
fqn,
(basename: string) =>
`${basename}\$${capitalizedEventName}EventParameters`,
),
eventTypeAliasNames: calculateDerivedNames(
fqn,
(basename: string) => `${basename}\$${capitalizedEventName}Event`,
),
};
};

Expand Down Expand Up @@ -254,19 +256,15 @@ function createEventParameterInterfaces(
if (symbol.events) {
symbol.events.forEach((event) => {
// build the interface (with no properties (event parameters) yet)
const {
fullEventParametersName,
shortEventParametersName,
fullEventTypealiasName,
shortEventTypealiasName,
} = makeEventParametersName(symbol.name, event.name);
const { eventParametersNames, eventTypeAliasNames } =
makeEventParametersName(symbol.name, event.name);
const eventParametersInterface: InterfaceSymbol = {
kind: "interface",
name: fullEventParametersName,
basename: shortEventParametersName,
name: eventParametersNames.name,
basename: eventParametersNames.basename,
module: symbol.module,
resource: symbol.resource,
export: shortEventParametersName,
export: eventParametersNames.exportName,
visibility: symbol.visibility,
__isNotAMarkerInterface: true,
};
Expand All @@ -290,7 +288,7 @@ function createEventParameterInterfaces(
eventParametersInterface.extends = makeEventParametersName(
superClass.name,
superEvent.name,
).fullEventParametersName;
).eventParametersNames.name;
}

// get inherited parameters to check compatibility and remove them from the new inheriting interface
Expand Down Expand Up @@ -338,25 +336,25 @@ function createEventParameterInterfaces(
// now also create a type alias for the event with these parameters and the concrete event source type
const eventTypedef: TypedefSymbol = {
kind: "typedef",
name: fullEventTypealiasName,
name: eventTypeAliasNames.name,
type: {
kind: "TypeReference",
typeName: "sap.ui.base.Event",
typeArguments: [
{
kind: "TypeReference",
typeName: shortEventParametersName,
typeName: eventParametersNames.basename,
},
{
kind: "TypeReference",
typeName: symbol.basename,
},
],
},
basename: shortEventTypealiasName,
basename: eventTypeAliasNames.basename,
module: symbol.module,
resource: symbol.resource,
export: shortEventTypealiasName,
export: eventTypeAliasNames.exportName,
visibility: symbol.visibility,
};
eventTypedef.description = `Event object of the ${symbol.basename}#${event.name} event.`;
Expand All @@ -374,7 +372,7 @@ function createEventParameterInterfaces(
enrichEventMethods(
symbol,
event.name,
shortEventTypealiasName,
eventTypeAliasNames.basename,
eventParametersInterface.name,
);

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