diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index d9f42a0d9..b7ae2dcc3 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -36,6 +36,7 @@ "@types/he": "^1.2.3", "@types/node": "^20.12.2", "@types/sinon": "^17.0.3", + "@types/unzip-stream": "^0.3.4", "@types/yargs": "^17.0.32", "@ui5-language-assistant/semantic-model": "^3.3.1", "@ui5-language-assistant/semantic-model-types": "^3.3.1", @@ -50,7 +51,8 @@ "semver": "^7.6.0", "sinon": "^17.0.1", "tsx": "^4.7.1", - "typescript-eslint": "^7.4.0" + "typescript-eslint": "^7.4.0", + "unzip-stream": "^0.3.1" }, "engines": { "node": "^18.14.2 || ^20.11.0 || >=21.2.0", @@ -2778,6 +2780,15 @@ "resolved": "https://registry.npmjs.org/@types/three/-/three-0.125.3.tgz", "integrity": "sha512-tUPMzKooKDvMOhqcNVUPwkt+JNnF8ASgWSsrLgleVd0SjLj4boJhteSsF9f6YDjye0mmUjO+BDMWW83F97ehXA==" }, + "node_modules/@types/unzip-stream": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@types/unzip-stream/-/unzip-stream-0.3.4.tgz", + "integrity": "sha512-ud0vtsNRF+joUCyvNMyo0j5DKX2Lh/im+xVgRzBEsfHhQYZ+i4fKTveova9XxLzt6Jl6G0e/0mM4aC0gqZYSnA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -4058,6 +4069,19 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -4142,6 +4166,15 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true, + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/builtins": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", @@ -4334,6 +4367,18 @@ "node": ">=12.19" } }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", @@ -11630,6 +11675,15 @@ "node": ">=8.0" } }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/treeverse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", @@ -11919,6 +11973,28 @@ "node": ">= 10.0.0" } }, + "node_modules/unzip-stream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/unzip-stream/-/unzip-stream-0.3.1.tgz", + "integrity": "sha512-RzaGXLNt+CW+T41h1zl6pGz3EaeVhYlK+rdAap+7DxW5kqsqePO8kRtWPaCiVqdhZc86EctSPVYNix30YOMzmw==", + "dev": true, + "dependencies": { + "binary": "^0.3.0", + "mkdirp": "^0.5.1" + } + }, + "node_modules/unzip-stream/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", diff --git a/package.json b/package.json index 83852a389..8d45147cc 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,8 @@ "unit-debug": "ava debug", "unit-update-snapshots": "ava --update-snapshots", "unit-watch": "ava --watch", - "update-semantic-model-info": "tsx scripts/metadataProvider/createMetadataInfo.ts" + "update-semantic-model-info": "tsx scripts/metadataProvider/createMetadataInfo.ts", + "update-pseudo-modules-info": "tsx scripts/metadataProvider/createPseudoModulesInfo.ts" }, "files": [ "CHANGELOG.md", @@ -83,6 +84,7 @@ "@types/he": "^1.2.3", "@types/node": "^20.12.2", "@types/sinon": "^17.0.3", + "@types/unzip-stream": "^0.3.4", "@types/yargs": "^17.0.32", "@ui5-language-assistant/semantic-model": "^3.3.1", "@ui5-language-assistant/semantic-model-types": "^3.3.1", @@ -97,6 +99,7 @@ "semver": "^7.6.0", "sinon": "^17.0.1", "tsx": "^4.7.1", - "typescript-eslint": "^7.4.0" + "typescript-eslint": "^7.4.0", + "unzip-stream": "^0.3.1" } } diff --git a/scripts/metadataProvider/createPseudoModulesInfo.ts b/scripts/metadataProvider/createPseudoModulesInfo.ts new file mode 100644 index 000000000..a81ccb592 --- /dev/null +++ b/scripts/metadataProvider/createPseudoModulesInfo.ts @@ -0,0 +1,139 @@ +import {pipeline} from "node:stream/promises"; +import {Extract} from "unzip-stream"; +import MetadataProvider from "./MetadataProvider.js"; +import {writeFile} from "node:fs/promises"; + +import type {UI5Enum} from "@ui5-language-assistant/semantic-model-types"; + +const API_JSON_LOCATION = "https://int.repositories.cloud.sap/artifactory/build-releases/com/sap/ui5/dist/sapui5-sdk-dist/{versionPlaceholder}/sapui5-sdk-dist-{versionPlaceholder}-api-jsons.zip"; +const RAW_API_JSON_FILES_FOLDER = "tmp/apiJson"; + +async function downloadAPIJsons(sapui5Version: string) { + const url = API_JSON_LOCATION.replaceAll("{versionPlaceholder}", sapui5Version); + + const response = await fetch(url); + if (!response.ok) { + throw new Error(`unexpected response ${response.statusText}`); + } + + if (response.body && response.body instanceof ReadableStream) { + await pipeline(response.body, Extract({path: RAW_API_JSON_FILES_FOLDER})); + } else { + throw new Error("Malformed response"); + } +} + +async function transformFiles() { + const metadataProvider = new MetadataProvider(); + await metadataProvider.init(RAW_API_JSON_FILES_FOLDER); + + const {enums} = metadataProvider.getModel(); + + const groupedEnums = Object.keys(enums).reduce((acc: Record, enumKey: string) => { + const curEnum = enums[enumKey]; + + acc[curEnum.library] = acc[curEnum.library] ?? []; + acc[curEnum.library].push(curEnum); + + return acc; + }, Object.create(null)); + + await addOverrides(groupedEnums); +} + +async function addOverrides(enums: Record) { + const indexFilesImports: string[] = []; + const buildJSDoc = (enumEntry: UI5Enum, indent: string = "") => { + const jsDocBuilder: string[] = [`${indent}/**`]; + + if (enumEntry.description) { + jsDocBuilder.push(`${indent} * ${enumEntry.description.replaceAll("\n", "\n" + indent + " * ")}`); + jsDocBuilder.push(`${indent} *`); + } + + if (enumEntry.experimentalInfo) { + let experimental: string = `${indent} * @experimental`; + if (enumEntry.experimentalInfo.since) { + experimental += ` (since ${enumEntry.experimentalInfo.since})`; + } + if (enumEntry.experimentalInfo.text) { + experimental += ` - ${enumEntry.experimentalInfo.text}`; + } + jsDocBuilder.push(experimental); + } + + if (enumEntry.deprecatedInfo) { + let deprecated: string = `${indent} * @deprecated`; + if (enumEntry.deprecatedInfo.since) { + deprecated += ` (since ${enumEntry.deprecatedInfo.since})`; + } + if (enumEntry.deprecatedInfo.text) { + deprecated += ` - ${enumEntry.deprecatedInfo.text}`; + } + jsDocBuilder.push(deprecated); + } + + if (enumEntry.visibility) { + jsDocBuilder.push(`${indent} * @${enumEntry.visibility}`); + } + + if (enumEntry.since) { + jsDocBuilder.push(`${indent} * @since ${enumEntry.since}`); + } + jsDocBuilder.push(`${indent}*/`); + + return jsDocBuilder.join("\n"); + } + + Object.keys(enums).forEach(async (libName) => { + const enumEntries = enums[libName]; + + let stringBuilder: string[] = [ + `declare module "${libName.replaceAll(".", "/")}/library" {` + ]; + enumEntries.forEach((enumEntry) => { + if (enumEntry.kind !== "UI5Enum") { + return; + } + + stringBuilder.push(buildJSDoc(enumEntry, "\t")); + stringBuilder.push(`\texport enum ${enumEntry.name} {`); + enumEntry.fields.forEach((value) => { + stringBuilder.push(buildJSDoc(value, "\t\t")); + stringBuilder.push(`\t\t${value.name} = "${value.name}",`); + }); + stringBuilder.push(`\t}`); + + return stringBuilder.join("\n"); + }); + stringBuilder.push(`}`) + + indexFilesImports.push(`import "./${libName}";`); + await writeFile( + new URL(`../../resources/overrides/library/${libName}.d.ts`, import.meta.url), + stringBuilder.join("\n") + ); + }); + + await writeFile( + new URL(`../../resources/overrides/library/index.d.ts`, import.meta.url), + indexFilesImports.join("\n") + ); +} + +async function main(sapui5Version: string) { + await downloadAPIJsons(sapui5Version); + + await transformFiles(); +} + +try { + const sapui5Version = process.argv[2]; + if (!sapui5Version) { + throw new Error("second argument \"sapui5Version\" is missing"); + } + await main(sapui5Version); +} catch (err) { + process.stderr.write(String(err)); + process.exit(1); +} diff --git a/scripts/metadataProvider/model.ts b/scripts/metadataProvider/model.ts index ad50f7c00..f0dac37ad 100644 --- a/scripts/metadataProvider/model.ts +++ b/scripts/metadataProvider/model.ts @@ -71,7 +71,7 @@ export async function createSemanticModel(apiJsonsRoot: string): Promise {}; } model = generate({ - version: "1.120.9", + version: "1.120.11", libraries: indexJson, typeNameFix: getTypeNameFix(), strict: false, // Throw instead of log errors