Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion lib/src/plugins/controller/controller_plugin_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,25 @@ extension ControllerPluginUtils on ControllerPlugin {
bool withState,
) async {
final imports = <String>[
'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');
}

Comment on lines +61 to +79

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Build the entity import from config.nameSnake.

Line 77 uses domainSnake for both the entity directory and file name. If domain differs from the entity name, a Product controller can import domain/entities/catalog/catalog.dart instead of domain/entities/product/product.dart. ProductPatch and ProductFields then remain unresolved.

Use config.nameSnake for the entity directory and file name.

Proposed fix
-      imports.add('../../../domain/entities/$domainSnake/$domainSnake.dart');
+      imports.add(
+        '../../../domain/entities/${config.nameSnake}/${config.nameSnake}.dart',
+      );

Based on learnings, the fixed v5 entity layout is lib/src/domain/entities/{entity_snake}/{entity_snake}.dart, for example lib/src/domain/entities/product/product.dart.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// #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');
}
// #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/${config.nameSnake}/${config.nameSnake}.dart',
);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/src/plugins/controller/controller_plugin_utils.dart` around lines 61 -
79, Update the entity import construction in the !config.noEntity block to use
config.nameSnake for both the entity directory and Dart file name, ensuring
generated controller signatures resolve the matching entity types.

Source: Learnings

if (withState && !config.noEntity) {
if (config.generateState) {
imports.add('${config.nameSnake}_state.dart');
Expand Down
14 changes: 13 additions & 1 deletion lib/src/plugins/di/di_plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>().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<String>().toList() ??
(context.get<bool>('no-entity') == true
? []
: ['get', 'update', 'toggle']),
Comment on lines +104 to +116

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep the default method contract consistent with downstream allowlists.

Both changed defaults advertise toggle, but the downstream allowlists filter it out. Add toggle to both allowlists.

  • lib/src/plugins/di/di_plugin.dart#L104-L116: add toggle to validMethods in _generateEntityUseCaseDIFiles at Lines 938-947.
  • lib/src/plugins/test/test_plugin.dart#L85-L93: add toggle to validMethods in generate at Lines 155-165.

This is required for the PR objective that default get, update, and toggle methods generate per-method DI and test files.

📍 Affects 2 files
  • lib/src/plugins/di/di_plugin.dart#L104-L116 (this comment)
  • lib/src/plugins/test/test_plugin.dart#L85-L93
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/src/plugins/di/di_plugin.dart` around lines 104 - 116, The default
methods include toggle, but downstream allowlists exclude it. In
lib/src/plugins/di/di_plugin.dart lines 104-116, update validMethods in
_generateEntityUseCaseDIFiles to allow toggle; in
lib/src/plugins/test/test_plugin.dart lines 85-93, update validMethods in
generate likewise, so default get, update, and toggle methods produce per-method
DI and test files.

domain: context.data['domain'],
repo: context.data['repo'],
service: context.data['service'],
Expand Down
5 changes: 4 additions & 1 deletion lib/src/plugins/presenter/presenter_plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,10 @@ class PresenterPlugin extends FileGeneratorPlugin implements CliAwarePlugin {
bool useDi,
FileSystem fs,
) async {
final imports = <String>['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 = <String>['package:zuraffa_flutter/zuraffa_flutter.dart'];

if (config.isCustomUseCase || config.isOrchestrator) {
final types = <String>[];
Expand Down
13 changes: 12 additions & 1 deletion lib/src/plugins/repository/repository_plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
10 changes: 9 additions & 1 deletion lib/src/plugins/test/test_plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>().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<String>().toList() ??
(context.get<bool>('no-entity') == true
? []
: ['get', 'update', 'toggle']),
usecases: context.data['usecases']?.cast<String>().toList() ?? [],
variants: context.data['variants']?.cast<String>().toList() ?? [],
noEntity: context.get<bool>('no-entity') ?? false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
5 changes: 4 additions & 1 deletion lib/src/plugins/view/view_plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading