|
| 1 | +# Project Info |
| 2 | + |
| 3 | +## Setup |
| 4 | + |
| 5 | +```bash |
| 6 | +git submodule update --init --recursive |
| 7 | +dart pub global activate -spath packages/aft |
| 8 | +aft bootstrap |
| 9 | +``` |
| 10 | + |
| 11 | +## Linting |
| 12 | + |
| 13 | +Changes MUST pass: |
| 14 | +``` |
| 15 | +flutter analyze |
| 16 | +``` |
| 17 | + |
| 18 | +### Auto format: |
| 19 | +``` |
| 20 | +dart pub get # crucial to run this! |
| 21 | +dart fix --apply . |
| 22 | +dart format . |
| 23 | +``` |
| 24 | + |
| 25 | +## Project Coding Conventions & Patterns Summary |
| 26 | + |
| 27 | +### Project Overview |
| 28 | +- **Language**: Dart (Flutter + pure Dart) |
| 29 | +- **Architecture**: Monorepo with ~25+ packages under `packages/` |
| 30 | +- **Tooling**: Custom `aft` (Amplify Flutter Tool) for bootstrapping, formatting, analysis, testing |
| 31 | +- Root `pubspec.yaml` - Flutter/Dart monorepo configuration with multiple packages |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +### 1. File & Directory Naming |
| 36 | +- **snake_case** for all Dart files: `auth_plugin_impl.dart`, `state_machine.dart`, `amplify_exception.dart` |
| 37 | +- **snake_case** for directories: `amplify_core`, `aws_common`, `amplify_auth_cognito_dart` |
| 38 | +- Package names follow pattern: `amplify_<category>` for Flutter, `amplify_<category>_dart` for pure Dart |
| 39 | +- Generated files use `.g.dart` suffix (json_serializable) |
| 40 | +- Platform-conditional files use suffixes: `globals.flutter.dart`, `globals.dart.dart`, `initial_parameters_stub.dart` / `initial_parameters_html.dart` |
| 41 | + |
| 42 | +### 2. License Header (Required on ALL files) |
| 43 | +```dart |
| 44 | +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 45 | +// SPDX-License-Identifier: Apache-2.0 |
| 46 | +``` |
| 47 | +Enforced via `tool/license.sh` and CI checks. |
| 48 | + |
| 49 | +### 3. Code Style & Lint Rules |
| 50 | +- Every `analysis_options.yaml` MUST include a shared lint profile from `packages/amplify_lints/`: |
| 51 | + - **Library packages**: `include: package:amplify_lints/library.yaml` |
| 52 | + - **Example/app packages**: `include: package:amplify_lints/app.yaml` |
| 53 | +- The profiles handle all lint rules, strict mode settings, and enforced conventions. Packages may add local overrides (e.g., excluding `.g.dart` files) but must not replace the base include. |
| 54 | + |
| 55 | +### 4. Documentation Patterns |
| 56 | +- **Dartdoc template/macro system** used extensively: |
| 57 | + ```dart |
| 58 | + /// {@template amplify_core.amplify_exception} |
| 59 | + /// Description here. |
| 60 | + /// {@endtemplate} |
| 61 | + ``` |
| 62 | + Then referenced elsewhere: |
| 63 | + ```dart |
| 64 | + /// {@macro amplify_core.amplify_exception} |
| 65 | + ``` |
| 66 | +- **Category tags** for dartdoc categorization: `/// {@category Auth}` |
| 67 | +- All public APIs have doc comments (enforced by `public_member_api_docs`) |
| 68 | +- Code examples in docs use `<?code-excerpt>` syntax for verified code excerpts |
| 69 | + |
| 70 | +### 5. Architecture Patterns |
| 71 | + |
| 72 | +#### 5a. Category/Plugin Architecture |
| 73 | +- **Categories**: Abstract interfaces (`AuthCategory`, `StorageCategory`, etc.) define the public API |
| 74 | +- **Plugins**: Concrete implementations (`AmplifyAuthCognitoDart`) implement category interfaces |
| 75 | +- **Plugin Key pattern**: Static `pluginKey` constant for type-safe plugin retrieval: |
| 76 | + ```dart |
| 77 | + static const AuthPluginKey<AmplifyAuthCognitoDart> pluginKey = _AmplifyAuthCognitoDartPluginKey(); |
| 78 | + ``` |
| 79 | + |
| 80 | +#### 5b. State Machine Pattern (Core architectural pattern) |
| 81 | +- Extensive use of typed state machines for complex flows (auth, sign-in, sign-out, etc.) |
| 82 | +- Hierarchy: `StateMachineManager` → `StateMachine` → `StateMachineEvent` / `StateMachineState` |
| 83 | +- States are `sealed` classes with named constructors for each variant: |
| 84 | + ```dart |
| 85 | + sealed class SignInState extends AuthState<SignInStateType> { |
| 86 | + const factory SignInState.notStarted() = SignInNotStarted; |
| 87 | + const factory SignInState.success(AuthUser user) = SignInSuccess; |
| 88 | + const factory SignInState.failure({...}) = SignInFailure; |
| 89 | + } |
| 90 | + ``` |
| 91 | +- State types expressed as enums: `SignInStateType { notStarted, initiating, challenge, success, failure }` |
| 92 | +- `SuccessState` and `ErrorState` mixins for terminal states |
| 93 | +- `EventCompleter<E, S>` for async event tracking with `accepted`/`completed` futures |
| 94 | +- Events have `checkPrecondition()` method for guard conditions |
| 95 | + |
| 96 | +#### 5c. Dependency Injection |
| 97 | +- `DependencyManager` (service locator pattern) with `addBuilder()`, `addInstance()`, `get()`, `getOrCreate()` |
| 98 | +- `Token<T>` for type-safe dependency keys |
| 99 | +- `_ScopedDependencyManager` for hierarchical scope |
| 100 | + |
| 101 | +#### 5d. Dart/Flutter Split |
| 102 | +- Pure Dart packages (`_dart` suffix) for cross-platform logic |
| 103 | +- Flutter packages wrap Dart packages for platform integration |
| 104 | +- Conditional imports for web vs. VM: `if (dart.library.js_interop)` |
| 105 | + |
| 106 | +### 6. Type System Patterns |
| 107 | + |
| 108 | +#### 6a. Mixins as Core Building Blocks |
| 109 | +- `AWSEquatable<T>` — Value equality via `props` list (like Equatable package) |
| 110 | +- `AWSDebuggable` — Safe `toString()` via `runtimeTypeName` (prevents runtime reflection) |
| 111 | +- `AWSSerializable<T>` — `toJson()` contract |
| 112 | +- `AmplifyLoggerMixin` — Logging integration |
| 113 | +- Classes commonly compose multiple mixins: |
| 114 | + ```dart |
| 115 | + class AmplifyOutputs with AWSEquatable<AmplifyOutputs>, AWSSerializable, AWSDebuggable { ... } |
| 116 | + ``` |
| 117 | + |
| 118 | +#### 6b. Sealed Classes & Pattern Matching |
| 119 | +- Heavy use of Dart 3 sealed classes for states and results: |
| 120 | + ```dart |
| 121 | + sealed class AWSResult<V, E extends Exception> { ... } |
| 122 | + final class AWSSuccessResult<V, E> extends AWSResult<V, E> { ... } |
| 123 | + final class AWSErrorResult<V, E> extends AWSResult<V, E> { ... } |
| 124 | + ``` |
| 125 | +- `final class` for concrete implementations (prevent extension) |
| 126 | +- `base class` / `base mixin` for state machine types (controlled hierarchy) |
| 127 | +- Dart 3 pattern matching (`switch` expressions, `case` patterns) used throughout |
| 128 | + |
| 129 | +#### 6c. Immutability |
| 130 | +- `@immutable` annotation on value types |
| 131 | +- `const` constructors used wherever possible |
| 132 | +- `prefer_final_locals` enforced by lints |
| 133 | + |
| 134 | +### 7. Error Handling |
| 135 | +- `AmplifyException` (recoverable) vs `AmplifyError` (non-recoverable) distinction |
| 136 | +- Exceptions include `message`, `recoverySuggestion`, `underlyingException` |
| 137 | +- Category-specific exception hierarchies via `part` files |
| 138 | +- `PreconditionException` for state machine guard failures |
| 139 | +- `only_throw_errors` lint: only throw `Exception`/`Error` subclasses |
| 140 | + |
| 141 | +### 8. Serialization |
| 142 | +- `json_serializable` + `json_annotation` for JSON serialization |
| 143 | +- Shared serialization options via constants: |
| 144 | + - `zAmplifySerializable` — Standard Amplify types (`includeIfNull: false`, `explicitToJson: true`) |
| 145 | + - `zAwsSerializable` — AWS types (`fieldRename: FieldRename.pascal`) |
| 146 | + - `zAmplifyOutputsSerializable` — Amplify Outputs (`fieldRename: FieldRename.snake`) |
| 147 | +- Generated code in `.g.dart` files, excluded from analysis |
| 148 | + |
| 149 | +### 9. Naming Conventions |
| 150 | +- **Constants**: `lowerCamelCase` with `z` prefix for internal/library-wide constants: `zAmplifySerializable`, `zIsFlutter`, `zDefaultLogLevel`, `zAssertsEnabled` |
| 151 | +- **Enums**: `PascalCase` type, `lowerCamelCase` values |
| 152 | +- Use visibility annotations (`@protected`, `@visibleForTesting`, `@internal`) from `package:meta` |
| 153 | +- **Factory constructors**: Named after source — `fromJson`, `fromMap` |
| 154 | + |
| 155 | +### 10. Testing Patterns |
| 156 | +- Tests use `package:test` (not `flutter_test`) for pure Dart |
| 157 | +- Standard `group`/`test` structure |
| 158 | +- `@TestOn('vm')` or `@TestOn('browser')` for platform-specific tests |
| 159 | +- `MockAWSHttpClient` for HTTP mocking |
| 160 | +- `@visibleForTesting` setters/properties for test hooks |
| 161 | +- `zAssertsEnabled` guard for test-only code paths |
| 162 | + |
| 163 | +### 11. Library Declarations |
| 164 | +- Modern `library;` syntax (unnamed libraries) in Dart 3 |
| 165 | +- `part`/`part of` used for tightly-coupled files (e.g., state machine states/events grouped via parts) |
| 166 | +- Barrel files use explicit `export` with `show`/`hide` for API control |
| 167 | + |
| 168 | +### 12. Monorepo Management |
| 169 | +- Root `pubspec.yaml` defines shared dependency versions |
| 170 | +- `aft bootstrap` creates `pubspec_overrides.yaml` for local development |
| 171 | +- Components are grouped for coordinated versioning (e.g., all `Amplify Flutter` packages version together) |
| 172 | +- Semantic versioning followed; new enum cases = minor version bump |
0 commit comments