From 7176c112e4d4afdce478a6c633b77ad0b5773a29 Mon Sep 17 00:00:00 2001 From: Sweet-Dev-star Date: Mon, 29 Jun 2026 02:17:21 +0900 Subject: [PATCH] fix(schematics): avoid self-import for same-namespace generic base type argument When an Angular proxy DTO inherits from a generic base type such as PagedResultDto and the type argument lives in the same namespace, the model generator emitted an invalid self-import inside models.ts (e.g. `import type { TransactionDto } from './models'`), breaking the Angular/TypeScript build. The base-type generic arguments were not filtered by namespace the way property refs already are: the outer guard only checked the base type's own namespace (Volo.Abp...PagedResultDto), so every type in the generic tree was imported, including same-namespace ones. Skip generic arguments whose namespace equals the model's, mirroring the existing property handling. The framework base type is still imported from @abp/ng.core; only the bogus self-import is dropped. Adds a regression test reproducing the reported scenario. Resolves #25080 Co-Authored-By: Claude Opus 4.8 --- .../proxy-self-import-generic-base.spec.ts | 82 +++++++++++++++++++ .../packages/schematics/src/utils/model.ts | 8 ++ 2 files changed, 90 insertions(+) create mode 100644 npm/ng-packs/packages/schematics/src/tests/proxy-self-import-generic-base.spec.ts diff --git a/npm/ng-packs/packages/schematics/src/tests/proxy-self-import-generic-base.spec.ts b/npm/ng-packs/packages/schematics/src/tests/proxy-self-import-generic-base.spec.ts new file mode 100644 index 0000000000..680e9f6f5d --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/tests/proxy-self-import-generic-base.spec.ts @@ -0,0 +1,82 @@ +import { describe, expect, it } from 'vitest'; +import { createImportRefsToModelReducer } from '../utils/model'; +import { Type } from '../models/api-definition'; +import { ModelGeneratorParams } from '../utils/model'; + +// Reproduces #25080: when a DTO inherits from a generic base type (e.g. PagedResultDto) +// whose type argument T lives in the SAME namespace, the proxy generator used to emit an +// invalid self-import (`import type { TransactionDto } from './models'`) inside models.ts, +// breaking the Angular/TypeScript build. +describe('proxy generation - self-import for generic base type argument (#25080)', () => { + const solution = 'MyApp'; + + // TransactionPagedResultDto : PagedResultDto, both in MyApp.Transactions. + const types: Record = { + 'MyApp.Transactions.TransactionDto': { + baseType: null, + isEnum: false, + enumNames: null, + enumValues: null, + genericArguments: null, + properties: [ + { + name: 'Id', + jsonName: null, + type: 'System.Guid', + typeSimple: 'System.Guid', + isRequired: false, + isNullable: false, + }, + ], + }, + 'MyApp.Transactions.TransactionPagedResultDto': { + baseType: 'Volo.Abp.Application.Dtos.PagedResultDto', + isEnum: false, + enumNames: null, + enumValues: null, + genericArguments: null, + properties: [ + { + name: 'AmountSummary', + jsonName: null, + type: 'System.Int32', + typeSimple: 'number', + isRequired: false, + isNullable: false, + }, + ], + }, + }; + + const params: ModelGeneratorParams = { + targetPath: 'src/app/proxy', + solution, + types, + serviceImports: {}, + modelImports: {}, + }; + + it('does not emit a self-import for a same-namespace generic base argument', () => { + const reduce = createImportRefsToModelReducer(params); + const models = reduce([], ['MyApp.Transactions.TransactionPagedResultDto']); + + const model = models.find(m => m.namespace === 'Transactions'); + expect(model).toBeDefined(); + + // The buggy behavior produced an import with path './models' (a self-import). + const selfImports = model!.imports.filter(i => i.path === './models'); + expect(selfImports).toEqual([]); + + // Sanity: TransactionDto must NOT be imported at all, since it is declared in this same file. + const importsTransactionDto = model!.imports.some(i => + i.specifiers.some(s => s === 'TransactionDto'), + ); + expect(importsTransactionDto).toBe(false); + + // The framework base type, however, should still be imported from @abp/ng.core. + const importsPagedResultDto = model!.imports.some( + i => i.path === '@abp/ng.core' && i.specifiers.includes('PagedResultDto'), + ); + expect(importsPagedResultDto).toBe(true); + }); +}); diff --git a/npm/ng-packs/packages/schematics/src/utils/model.ts b/npm/ng-packs/packages/schematics/src/utils/model.ts index 4e5431da60..8d218160ae 100644 --- a/npm/ng-packs/packages/schematics/src/utils/model.ts +++ b/npm/ng-packs/packages/schematics/src/utils/model.ts @@ -74,6 +74,14 @@ export function createImportRefsToModelReducer(params: ModelGeneratorParams) { if (baseType && parseNamespace(solution, baseType) !== model.namespace) { const baseTypeWithGenericParams = parseBaseTypeWithGenericTypes(baseType); baseTypeWithGenericParams.forEach(t => { + // A generic argument of the base type (e.g. T in PagedResultDto) may live in the + // same namespace as this model, which means it is generated into the same models.ts + // file. Importing it would produce an invalid self-import (`from './models'`), so we + // skip same-namespace types here, mirroring the property handling below. See #25080. + if (parseNamespace(solution, t) === model.namespace) { + return; + } + toBeImported.push({ type: t, isEnum: false,