From 13cff5b97ef3b960ca3506f1df044265bd622f26 Mon Sep 17 00:00:00 2001 From: akudev Date: Fri, 29 May 2026 15:55:28 +0200 Subject: [PATCH] fix(dts-generator): add @types discovery to generate check-compile path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TS6 no longer auto-includes @types packages when the compiler is invoked programmatically. The previous fix (631f1de) only addressed runCheck.ts but the generation path in generate.ts has the same problem — causing "Cannot find namespace 'THREE'" errors for sap.ui.vk in the legacy-free build. Extracted the @types discovery logic into a shared utility (utils/discover-types.ts) and applied it to both generate.ts and runCheck.ts. Also aligned generate.ts module settings to ESNext + Bundler to prevent bare-specifier rejection of ambient declare module declarations. --- .../dts-generator/src/generate-from-paths.ts | 12 ++--- packages/dts-generator/src/generate.ts | 32 +++++------- packages/dts-generator/src/runCheck.ts | 52 ++----------------- .../src/utils/arguments-index.ts | 8 ++- .../dts-generator/src/utils/discover-types.ts | 51 ++++++++++++++++++ 5 files changed, 75 insertions(+), 80 deletions(-) create mode 100644 packages/dts-generator/src/utils/discover-types.ts diff --git a/packages/dts-generator/src/generate-from-paths.ts b/packages/dts-generator/src/generate-from-paths.ts index e5cbf72..052100e 100644 --- a/packages/dts-generator/src/generate-from-paths.ts +++ b/packages/dts-generator/src/generate-from-paths.ts @@ -56,15 +56,9 @@ export interface GenerateFromPathsConfig { dependenciesDTSPathForCheck: string; /** - * Comma-separated list of package names of the libraries on which the currently to-be-built types depends. - * - * This is meant for entire npm packages developed separately (often by others), not sibling libraries built in the same batch. - * E.g. when a custom UI5 control library is built by an application team, then it usually depends on the OpenUI5 types because - * those define the base classes like Control. - * Setting this has the effect that for the TS compilation check, the `types` field of the package.json file will be set to the - * respective package names and any other type packages are no longer considered. - * - * Only needed for the check. + * @deprecated Since 4.0.3. Declared @types/* dependencies are now auto-discovered + * from the nearest package.json. This option is still accepted for backward + * compatibility and merges additively with the discovered types. */ dependenciesTypePackagesForCheck?: string; diff --git a/packages/dts-generator/src/generate.ts b/packages/dts-generator/src/generate.ts index 253bf31..afbe054 100644 --- a/packages/dts-generator/src/generate.ts +++ b/packages/dts-generator/src/generate.ts @@ -5,6 +5,7 @@ import ts from "typescript"; import { generateFromObjects, Directives } from "./generate-from-objects.js"; import { default as checkCompile } from "./checkCompile/check-compile.js"; import { writeFileSafe, loadJSON } from "./utils/file-utils.js"; +import { discoverTypes } from "./utils/discover-types.js"; const loadedCache = new Map(); @@ -98,15 +99,9 @@ export type GenerateConfig = { dependencyDTSFilesForCheck: string[]; /** - * Array of package names of the libraries on which the currently to-be-built types depends. - * - * This is meant for entire npm packages developed separately (often by others), not sibling libraries built in the same batch. - * E.g. when a custom UI5 control library is built by an application team, then it usually depends on the OpenUI5 types because - * those define the base classes like Control. - * Setting this has the effect that for the TS compilation check, the `types` field of the package.json file will be set to the - * respective package names and any other type packages are no longer considered. - * - * Only needed for the check. + * @deprecated Since 4.0.3. Declared @types/* dependencies are now auto-discovered + * from the nearest package.json. This option is still accepted for backward + * compatibility and merges additively with the discovered types. */ dependenciesTypePackagesForCheck?: string[]; @@ -170,23 +165,24 @@ export async function generate({ ); // set up the tsconfig for the test compile + const discovered = discoverTypes(); const tsOptions: ts.BuildOptions = { noEmit: true, noImplicitAny: true, strict: true, target: ts.ScriptTarget.ES2015, - module: ts.ModuleKind.ES2015, + module: ts.ModuleKind.ESNext, + moduleResolution: ts.ModuleResolutionKind.Bundler, lib: ["lib.es2015.d.ts", "lib.dom.d.ts"], + ...(discovered.typeRoots.length > 0 && { + typeRoots: discovered.typeRoots, + types: [ + ...discovered.types, + ...(dependenciesTypePackagesForCheck || []), + ], + }), }; - // if type dependencies are set, use them - if ( - dependenciesTypePackagesForCheck && - dependenciesTypePackagesForCheck.length > 0 - ) { - tsOptions.types = dependenciesTypePackagesForCheck; - } - const success = checkCompile({ mainFile: targetFile, dependencyFiles: dependencyDTSFilesForCheck, diff --git a/packages/dts-generator/src/runCheck.ts b/packages/dts-generator/src/runCheck.ts index b553681..5acaaf1 100644 --- a/packages/dts-generator/src/runCheck.ts +++ b/packages/dts-generator/src/runCheck.ts @@ -3,7 +3,7 @@ const log = getLogger("@ui5/dts-generator/runCheck"); import esMain from "es-main"; import * as path from "path"; -import { promises as fsp, readdirSync, readFileSync } from "fs"; +import { promises as fsp } from "fs"; const readdir = fsp.readdir; import { @@ -13,6 +13,7 @@ import { ModuleKind, ModuleResolutionKind, } from "./index.js"; +import { discoverTypes } from "./utils/discover-types.js"; async function findFiles(dir: string, extension: string) { if (dir == null) { @@ -48,52 +49,7 @@ async function main() { const dtsFiles = await findFiles(dtsDir, "d.ts"); - // TS6 no longer auto-includes @types packages; discover which ones are declared - // as dependencies and walk up from CWD to find where they're installed. - const declaredTypes = new Set(); - let dir = process.cwd(); - while (true) { - try { - const pkg = JSON.parse( - readFileSync(path.join(dir, "package.json"), "utf8"), - ); - for (const deps of [pkg.dependencies, pkg.devDependencies]) { - if (deps) { - for (const name of Object.keys(deps)) { - if (name.startsWith("@types/")) { - declaredTypes.add(name.slice("@types/".length)); - } - } - } - } - break; - } catch { - // no package.json at this level - } - const parent = path.dirname(dir); - if (parent === dir) break; - dir = parent; - } - - const typeRoots: string[] = []; - const types = new Set(); - dir = process.cwd(); - while (true) { - const candidate = path.join(dir, "node_modules", "@types"); - try { - for (const entry of readdirSync(candidate, { withFileTypes: true })) { - if (entry.isDirectory() && declaredTypes.has(entry.name)) { - types.add(entry.name); - } - } - typeRoots.push(candidate); - } catch { - // doesn't exist at this level - } - const parent = path.dirname(dir); - if (parent === dir) break; - dir = parent; - } + const { typeRoots, types } = discoverTypes(); log.verbose(`Running a compile check for ${dtsFiles}`); const success = checkCompile({ @@ -105,7 +61,7 @@ async function main() { target: ScriptTarget.ES2015, module: ModuleKind.ESNext, moduleResolution: ModuleResolutionKind.Bundler, - ...(typeRoots.length > 0 && { typeRoots, types: [...types] }), + ...(typeRoots.length > 0 && { typeRoots, types }), }, }); diff --git a/packages/dts-generator/src/utils/arguments-index.ts b/packages/dts-generator/src/utils/arguments-index.ts index 3fc6250..aee5823 100644 --- a/packages/dts-generator/src/utils/arguments-index.ts +++ b/packages/dts-generator/src/utils/arguments-index.ts @@ -19,11 +19,9 @@ export const args = (() => { }); parser.add_argument("--dependenciesTypePackagesForCheck", { help: - "Comma-separated list of package names of the libraries on which the currently to-be-built types depends. This is meant for entire npm packages" + - " developed separately (often by others), not sibling libraries built in the same generation run. E.g. when a custom UI5 control library is built by an" + - " application team, then it usually depends on the OpenUI5 types because those define the base classes like Control. Setting this has the effect" + - " that for the TS compilation check, the `types` field of the package.json file will be set to the respective package names and any other type packages" + - " are no longer considered. Only needed for the check.", + "[DEPRECATED: @types packages are now auto-discovered from the nearest package.json] " + + "Comma-separated list of additional @types package names to include in the TS compilation check." + + " Typically no longer needed — declared @types/* dependencies are discovered automatically.", }); parser.add_argument("--directivesPath", { help: "Directory where the .dtsgenrc files for the libraries (current and dependencies) are located.", diff --git a/packages/dts-generator/src/utils/discover-types.ts b/packages/dts-generator/src/utils/discover-types.ts new file mode 100644 index 0000000..c0c2d23 --- /dev/null +++ b/packages/dts-generator/src/utils/discover-types.ts @@ -0,0 +1,51 @@ +import * as path from "path"; +import { readdirSync, readFileSync } from "fs"; + +export function discoverTypes(): { typeRoots: string[]; types: string[] } { + const declaredTypes = new Set(); + let dir = process.cwd(); + while (true) { + try { + const pkg = JSON.parse( + readFileSync(path.join(dir, "package.json"), "utf8"), + ); + for (const deps of [pkg.dependencies, pkg.devDependencies]) { + if (deps) { + for (const name of Object.keys(deps)) { + if (name.startsWith("@types/")) { + declaredTypes.add(name.slice("@types/".length)); + } + } + } + } + break; + } catch { + // no package.json at this level + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + + const typeRoots: string[] = []; + const types = new Set(); + dir = process.cwd(); + while (true) { + const candidate = path.join(dir, "node_modules", "@types"); + try { + for (const entry of readdirSync(candidate, { withFileTypes: true })) { + if (entry.isDirectory() && declaredTypes.has(entry.name)) { + types.add(entry.name); + } + } + typeRoots.push(candidate); + } catch { + // doesn't exist at this level + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + + return { typeRoots, types: [...types] }; +}