From e3e1de988c00913f3bc991afbba0d51f60525a9f Mon Sep 17 00:00:00 2001 From: arrrrny Date: Wed, 12 Aug 2026 05:22:19 +0000 Subject: [PATCH] fix(zfa make): always generate data repo impl + wire per-method DI for entity presets (#284) --- .../controller/controller_plugin_utils.dart | 17 ++++++++++++++++- lib/src/plugins/di/di_plugin.dart | 14 +++++++++++++- lib/src/plugins/presenter/presenter_plugin.dart | 5 ++++- .../plugins/repository/repository_plugin.dart | 13 ++++++++++++- lib/src/plugins/test/test_plugin.dart | 10 +++++++++- .../adaptive_layout_scaffold_builder.dart | 2 +- lib/src/plugins/view/view_plugin.dart | 5 ++++- 7 files changed, 59 insertions(+), 7 deletions(-) diff --git a/lib/src/plugins/controller/controller_plugin_utils.dart b/lib/src/plugins/controller/controller_plugin_utils.dart index 82e78d12..b4b36270 100644 --- a/lib/src/plugins/controller/controller_plugin_utils.dart +++ b/lib/src/plugins/controller/controller_plugin_utils.dart @@ -58,10 +58,25 @@ extension ControllerPluginUtils on ControllerPlugin { bool withState, ) async { final imports = [ - 'package:zuraffa/zuraffa.dart', + // #284/#281: Presentation layer (controller/presenter/view) must import + // `zuraffa_flutter` (which re-exports `zuraffa` + Flutter-specific types + // like Controller/Presenter/CleanView) instead of `zuraffa` alone — + // generated Flutter apps depend on `zuraffa_flutter`, not `zuraffa`, + // and `Controller` only exists in the Flutter package. + 'package:zuraffa_flutter/zuraffa_flutter.dart', '${config.nameSnake}_presenter.dart', ]; + // #284: Always import the entity so ProductPatch/ProductFields and other + // generated entity types are in scope for the controller's method + // signatures (updateProduct takes ProductPatch, toggleProduct takes + // ProductFields). Previously this was only added inside the + // `withState && !noEntity` block, leaving entity-based controllers + // without the entity import when withState was false. + if (!config.noEntity) { + imports.add('../../../domain/entities/$domainSnake/$domainSnake.dart'); + } + if (withState && !config.noEntity) { if (config.generateState) { imports.add('${config.nameSnake}_state.dart'); diff --git a/lib/src/plugins/di/di_plugin.dart b/lib/src/plugins/di/di_plugin.dart index e3f51972..9aa4c428 100644 --- a/lib/src/plugins/di/di_plugin.dart +++ b/lib/src/plugins/di/di_plugin.dart @@ -101,7 +101,19 @@ class DiPlugin extends FileGeneratorPlugin implements CliAwarePlugin { force: context.core.force, verbose: context.core.verbose, revert: context.core.revert, - methods: context.data['methods']?.cast().toList() ?? [], + // #284: Apply the same entity-methods default the usecase/repository + // plugins use, so DI sees `isEntityBased=true` for canonical + // `zfa make Product --preset=crud` invocations and routes to + // _generateEntityUseCaseDIFiles (per-method DI files matching the + // per-method usecases the usecase plugin emits). Without this default, + // DI falls into the _generateCustomUseCaseDI branch and emits + // `product_usecase_di.dart` referencing a `ProductUseCase` + // class that is never generated, breaking the build. + methods: + context.data['methods']?.cast().toList() ?? + (context.get('no-entity') == true + ? [] + : ['get', 'update', 'toggle']), domain: context.data['domain'], repo: context.data['repo'], service: context.data['service'], diff --git a/lib/src/plugins/presenter/presenter_plugin.dart b/lib/src/plugins/presenter/presenter_plugin.dart index 72ae7e02..fcae9803 100644 --- a/lib/src/plugins/presenter/presenter_plugin.dart +++ b/lib/src/plugins/presenter/presenter_plugin.dart @@ -838,7 +838,10 @@ class PresenterPlugin extends FileGeneratorPlugin implements CliAwarePlugin { bool useDi, FileSystem fs, ) async { - final imports = ['package:zuraffa/zuraffa.dart']; + // #284/#281: Presentation layer imports `zuraffa_flutter` (which + // re-exports `zuraffa` + Flutter-specific Presenter type) instead of + // `zuraffa` alone — generated Flutter apps depend on `zuraffa_flutter`. + final imports = ['package:zuraffa_flutter/zuraffa_flutter.dart']; if (config.isCustomUseCase || config.isOrchestrator) { final types = []; diff --git a/lib/src/plugins/repository/repository_plugin.dart b/lib/src/plugins/repository/repository_plugin.dart index a4b16d11..339ee89d 100644 --- a/lib/src/plugins/repository/repository_plugin.dart +++ b/lib/src/plugins/repository/repository_plugin.dart @@ -187,7 +187,18 @@ class RepositoryPlugin extends FileGeneratorPlugin implements CliAwarePlugin { (config.appendToExisting && config.repo != null)) { files.add(await interfaceGen.generate(targetConfig)); } - if ((config.generateData || + // #284: Always emit the data repository implementation alongside the + // interface for entity-based configs. Previously this was gated on + // generateData || generateDataSource || appendToExisting, but those flags + // are not reliably set when the make invocation activates the datasource + // plugin via a preset (the schema default of false wins over the + // plugin-activation sync in PluginManager.buildContext). The DI plugin + // unconditionally emits `product_repository_di.dart` referencing + // `DataProductRepository` whenever generateRepository || generateData + // is true, so the impl must be produced for every entity-based config to + // keep the generated app compiling. + if ((config.isEntityBased || + config.generateData || config.generateDataSource || config.appendToExisting) && !config.hasService) { diff --git a/lib/src/plugins/test/test_plugin.dart b/lib/src/plugins/test/test_plugin.dart index 2e76a095..02791d5c 100644 --- a/lib/src/plugins/test/test_plugin.dart +++ b/lib/src/plugins/test/test_plugin.dart @@ -82,7 +82,15 @@ class TestPlugin extends FileGeneratorPlugin implements CliAwarePlugin { verbose: context.core.verbose, revert: context.core.revert, generateTest: true, - methods: context.data['methods']?.cast().toList() ?? [], + // #284: Apply the same entity-methods default the usecase/repository + // plugins use, so the test plugin routes to generateForMethod (per-method + // test files matching the per-method usecases) instead of generateCustom + // which looks for a non-existent `product_usecase.dart`. + methods: + context.data['methods']?.cast().toList() ?? + (context.get('no-entity') == true + ? [] + : ['get', 'update', 'toggle']), usecases: context.data['usecases']?.cast().toList() ?? [], variants: context.data['variants']?.cast().toList() ?? [], noEntity: context.get('no-entity') ?? false, diff --git a/lib/src/plugins/view/builders/adaptive_layout_scaffold_builder.dart b/lib/src/plugins/view/builders/adaptive_layout_scaffold_builder.dart index e76f1e60..253d4829 100644 --- a/lib/src/plugins/view/builders/adaptive_layout_scaffold_builder.dart +++ b/lib/src/plugins/view/builders/adaptive_layout_scaffold_builder.dart @@ -188,7 +188,7 @@ class AdaptiveLayoutScaffoldBuilder { }) { final stateReference = withState ? " and ../${stateSnake}_state.dart" : ''; return '''// Generated by zfa adaptive layout scaffold. -import 'package:zuraffa/zuraffa.dart'; +import 'package:zuraffa_flutter/zuraffa_flutter.dart'; import '../${controllerSnake}_controller.dart'; diff --git a/lib/src/plugins/view/view_plugin.dart b/lib/src/plugins/view/view_plugin.dart index f167e606..5eaa81af 100644 --- a/lib/src/plugins/view/view_plugin.dart +++ b/lib/src/plugins/view/view_plugin.dart @@ -665,7 +665,10 @@ class ViewPlugin extends FileGeneratorPlugin implements CliAwarePlugin { !config.isOrchestrator; if (!isCustom) { - imports.add('package:zuraffa/zuraffa.dart'); + // #284/#281: Presentation layer imports `zuraffa_flutter` (which + // re-exports `zuraffa` + Flutter-specific CleanView/CleanViewState/ + // ControlledWidgetBuilder types) instead of `zuraffa` alone. + imports.add('package:zuraffa_flutter/zuraffa_flutter.dart'); if (!useDi) { for (final repo in config.effectiveRepos) {