From 9deb13ab6fface11b653b798585264e88309c89d Mon Sep 17 00:00:00 2001 From: akudev Date: Thu, 6 Mar 2025 16:11:53 +0100 Subject: [PATCH 1/2] feat(dts-generator): allow "esmOnly" in directives and "readonly" props "esmOnly" can be used as property of entire overlays as well as of single methods and properties. An ObjectProperty in api.json or an overlay for it can now be "readonly". --- .../api-report/dts-generator.api.md | 4 ++- .../src/generate-from-objects.ts | 3 +- .../dts-generator/src/phases/dts-code-gen.ts | 1 + .../dts-generator/src/phases/json-fixer.ts | 29 +++++++++++++++---- .../dts-generator/src/phases/json-to-ast.ts | 6 +++- .../dts-generator/src/types/api-json.d.ts | 1 + packages/dts-generator/src/types/ast.d.ts | 1 + 7 files changed, 36 insertions(+), 9 deletions(-) diff --git a/packages/dts-generator/api-report/dts-generator.api.md b/packages/dts-generator/api-report/dts-generator.api.md index 61ad88d..c149177 100644 --- a/packages/dts-generator/api-report/dts-generator.api.md +++ b/packages/dts-generator/api-report/dts-generator.api.md @@ -52,7 +52,9 @@ export interface Directives { [orgNamespace: string]: true | [true] | [true, "keep_original_ns"]; }; overlays: { - [libraryName: string]: ConcreteSymbol[]; + [libraryName: string]: (ConcreteSymbol & { + esmOnly?: boolean; + })[]; }; typeTyposMap: { [orgType: string]: string; diff --git a/packages/dts-generator/src/generate-from-objects.ts b/packages/dts-generator/src/generate-from-objects.ts index f864ed4..4a16432 100644 --- a/packages/dts-generator/src/generate-from-objects.ts +++ b/packages/dts-generator/src/generate-from-objects.ts @@ -82,7 +82,7 @@ export interface Directives { * mapped to TypeScript and cannot be changed in a compatible way. */ overlays: { - [libraryName: string]: ConcreteSymbol[]; + [libraryName: string]: (ConcreteSymbol & { esmOnly?: boolean })[]; }; /** @@ -160,6 +160,7 @@ export async function generateFromObjects(config: GenerateFromObjectsConfig) { apiObject, actualOptions.dependencyApiObjects, actualOptions.directives, + actualOptions.generateGlobals, ); // Phase 2 - jsonToAst: diff --git a/packages/dts-generator/src/phases/dts-code-gen.ts b/packages/dts-generator/src/phases/dts-code-gen.ts index f69d680..8e22af2 100644 --- a/packages/dts-generator/src/phases/dts-code-gen.ts +++ b/packages/dts-generator/src/phases/dts-code-gen.ts @@ -630,6 +630,7 @@ function genField(ast: Variable) { text += JSDOC(ast) + NL; text += applyTsIgnore(ast); text += ast.static ? "static " : ""; + text += ast.readonly ? "readonly " : ""; text += `${ast.name} : ${ast.type ? genType(ast.type, "property") : "any"}` + NL; return text; diff --git a/packages/dts-generator/src/phases/json-fixer.ts b/packages/dts-generator/src/phases/json-fixer.ts index 8b3c3ed..226b1a2 100644 --- a/packages/dts-generator/src/phases/json-fixer.ts +++ b/packages/dts-generator/src/phases/json-fixer.ts @@ -31,7 +31,11 @@ const preparedCache = new Map(); * @param apijson * @param directives */ -function mergeOverlays(apijson: ApiJSON, directives: Directives) { +function mergeOverlays( + apijson: ApiJSON, + directives: Directives, + options = { generateGlobals: false }, +) { const overlays = directives.overlays && directives.overlays[apijson.library]; if (overlays == null) { return; // no overlays defined @@ -81,6 +85,10 @@ function mergeOverlays(apijson: ApiJSON, directives: Directives) { } overlays.forEach((overlay) => { + // do not generate esm-only symbols if generateGlobals is set + if (overlay.esmOnly && options.generateGlobals) { + return; + } log.verbose(` ${overlay.name}`); const symbol = find(overlay.name); if (symbol == null) { @@ -88,18 +96,25 @@ function mergeOverlays(apijson: ApiJSON, directives: Directives) { apijson.symbols.push(overlay); return; } - // overlay + // overlay needs to be merged into existing symbol Object.keys(overlay).forEach((prop) => { if (prop == "name") { return; // ignore name property, it's the 'primary key' and can't be changed } if (mapLikeProperties.has(prop)) { - if (!Array.isArray(symbol[prop])) { - symbol[prop] = overlay[prop]; + // = methods, properties + if (!Array.isArray(symbol[prop]) && Array.isArray(overlay[prop])) { + symbol[prop] = overlay[prop].filter( + (item) => !options.generateGlobals || !item.esmOnly, + ); return; } const symbolItems: ConcreteSymbol[] = symbol[prop]; overlay[prop].forEach((overlayItem: ConcreteSymbol) => { + if (overlayItem.esmOnly && options.generateGlobals) { + // skip esm-only ones in globals mode + return; + } const item = symbolItems.find( (symbolItem) => symbolItem.name === overlayItem.name && @@ -864,9 +879,9 @@ function markDeprecatedAliasesForEnums( function _prepareApiJson( json: ApiJSON, directives: Directives, - options = { mainLibrary: false }, + options = { mainLibrary: false, generateGlobals: false }, ) { - mergeOverlays(json, directives); + mergeOverlays(json, directives, { generateGlobals: options.generateGlobals }); substituteSapClassInfoTypedef(json); convertCoreAndConfigurationIntoANamespace(json, directives); moveTypeParametersFromConstructorToClass(json); @@ -904,10 +919,12 @@ export function fixApiJsons( targetLibJson: ApiJSON, dependencies: ApiJSON[], directives: Directives, + generateGlobals?: boolean, ) { // Part 1: "prepare JSON" let targetLibFixedJson = _prepareApiJson(targetLibJson, directives, { mainLibrary: true, + generateGlobals, }); let depsFixedJsons: ApiJSON[] = dependencies.map((depjson) => { diff --git a/packages/dts-generator/src/phases/json-to-ast.ts b/packages/dts-generator/src/phases/json-to-ast.ts index d987e58..3cd7e7d 100644 --- a/packages/dts-generator/src/phases/json-to-ast.ts +++ b/packages/dts-generator/src/phases/json-to-ast.ts @@ -1197,7 +1197,10 @@ const buildNestedTypedefs = _.partialRight( * @returns */ function buildVariable(property: ObjProperty): Variable { - assertKnownProps(["examples", "name", "type", "value", "optional"], property); + assertKnownProps( + ["examples", "name", "type", "value", "optional", "esmOnly", "readonly"], + property, + ); const astNode: Variable = { kind: "Variable", @@ -1205,6 +1208,7 @@ function buildVariable(property: ObjProperty): Variable { static: property.static === true, type: buildType(property.type), visibility: property.visibility, + readonly: property.readonly === true, optional: property.optional === true, }; diff --git a/packages/dts-generator/src/types/api-json.d.ts b/packages/dts-generator/src/types/api-json.d.ts index f9603e5..b03bd24 100644 --- a/packages/dts-generator/src/types/api-json.d.ts +++ b/packages/dts-generator/src/types/api-json.d.ts @@ -257,6 +257,7 @@ export interface ObjProperty { export?: string; resource?: string; visibility?: "public" | "protected" | "private" | "restricted"; + readonly?: boolean; static?: boolean; type: string; description?: string; diff --git a/packages/dts-generator/src/types/ast.d.ts b/packages/dts-generator/src/types/ast.d.ts index 02409e8..e1a8fe9 100644 --- a/packages/dts-generator/src/types/ast.d.ts +++ b/packages/dts-generator/src/types/ast.d.ts @@ -129,6 +129,7 @@ export interface Variable extends AstNode, UI5JSDocs { static?: boolean; type: Type; visibility: UI5Visibility; + readonly?: boolean; optional: boolean; } From 9e37c7b1a2181a4b0cccfc65999b09a561ee003b Mon Sep 17 00:00:00 2001 From: akudev Date: Wed, 12 Mar 2025 09:39:47 +0100 Subject: [PATCH 2/2] chore: review fix --- packages/dts-generator/src/phases/json-fixer.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/dts-generator/src/phases/json-fixer.ts b/packages/dts-generator/src/phases/json-fixer.ts index 226b1a2..bef4853 100644 --- a/packages/dts-generator/src/phases/json-fixer.ts +++ b/packages/dts-generator/src/phases/json-fixer.ts @@ -931,7 +931,10 @@ export function fixApiJsons( const key = `${depjson.library}-${depjson.version}`; let preparedjson = preparedCache.get(key); if (!preparedjson) { - preparedjson = _prepareApiJson(depjson, directives); + preparedjson = _prepareApiJson(depjson, directives, { + mainLibrary: false, + generateGlobals, + }); preparedCache.set(key, preparedjson); } return JSON.parse(JSON.stringify(preparedjson)); // cloning needed to avoid multiple processing within the subsequent steps; // TODO: but this can likely be improved