everything_stack_analyzer 0.1.0 copy "everything_stack_analyzer: ^0.1.0" to clipboard
everything_stack_analyzer: ^0.1.0 copied to clipboard

Analyzer plugin for Everything Stack - enforces schema evolution rules and best practices.

Everything Stack Analyzer #

Custom Dart analyzer plugin that enforces schema evolution rules for the Everything Stack framework.

This plugin is the correctness gate - it prevents developers from accidentally breaking schema compatibility through static analysis.

Why This Matters #

The Everything Stack uses versioning and snapshots to handle schema evolution safely. But the infrastructure is useless if developers can accidentally:

  • Add required fields to existing entities (breaks old snapshots)
  • Add new fields without defaults (deserialization fails)
  • Change field types (silent data loss)

This analyzer enforces the safe patterns, preventing these violations before code is committed.

Lints #

1. require_field_on_new_entity #

Severity: Warning Pattern: required field without default on entity classes

Problem: When you add a required field to an entity, old snapshots won't have that field. Deserialization will fail.

Example (BAD):

class Note extends BaseEntity {
  String title;       // v1
  String content;     // v1
  List<String> tags;  // v2 - REQUIRED! Old snapshots don't have this!
}

Fix:

class Note extends BaseEntity {
  String title;
  String content;
  List<String>? tags;  // v2 - Optional, defaults to null for old snapshots
}

Or provide a default:

List<String> tags = [];  // Default value provided

2. new_field_without_default_or_optional #

Severity: Warning Pattern: New field without JSON deserialization default in @JsonSerializable classes

Problem: When deserializing old snapshots, new fields won't exist in the JSON. Without a default value, the field becomes null unsafely.

Example (BAD):

@JsonSerializable()
class Note extends BaseEntity {
  String title;
  List<String> tags;  // NEW - missing from old snapshots!
}

When deserializing a v1 snapshot:

{
  "title": "Task",
  "content": "Do something"
  // No "tags" field!
}

The tags field will be uninitialized, violating null safety.

Fix - Option 1: Use @JsonKey with default:

@JsonSerializable()
class Note extends BaseEntity {
  String title;

  @JsonKey(defaultValue: [])
  List<String> tags;  // Default when missing from JSON
}

Fix - Option 2: Make optional:

@JsonSerializable()
class Note extends BaseEntity {
  String title;
  List<String>? tags;  // Null when missing from JSON
}

3. field_type_change_detection #

Severity: Info Pattern: Potential type mismatches on fields

Problem: Changing a field's type (e.g., Stringint) breaks backward compatibility. Old snapshots have the old type.

Example (BAD):

// v1 schema
class Note {
  String priority;  // "high", "medium", "low"
}

// v2 schema - someone changes to int
class Note {
  int priority;  // BREAKING! Old snapshots have String
}

Fix: Add a new field and deprecate the old one:

class Note {
  @Deprecated('Use priorityLevel instead')
  String? priority;

  @JsonKey(defaultValue: 0)
  int priorityLevel;  // New field with safe default
}

Installation #

Step 1: Add to pubspec.yaml #

In your main project's pubspec.yaml:

dev_dependencies:
  everything_stack_analyzer:
    path: ../everything_stack_analyzer

Step 2: Enable in analysis_options.yaml #

Add to analysis_options.yaml:

analyzer:
  plugins:
    - custom_lint

linter:
  rules:
    # Schema evolution rules (enforced by everything_stack_analyzer)
    - require_field_on_new_entity
    - new_field_without_default_or_optional
    - field_type_change_detection

Step 3: Run analyzer #

flutter analyze

All three lints will run automatically.


How It Works #

Architecture #

The analyzer is built on:

  • custom_lint - Dart's official plugin system for custom lint rules
  • analyzer - The Dart analyzer AST for static code analysis

Each lint rule:

  1. Walks the AST - Examines class declarations, fields, annotations
  2. Detects patterns - Looks for unsafe schema evolution patterns
  3. Reports errors - Highlights violations with actionable messages

Rule Implementation #

Example: require_field_on_new_entity

class RequiredFieldOnNewEntity extends DartLintRule {
  @override
  void run(CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context) {
    // 1. Find all classes that extend BaseEntity
    context.registry.addClassDeclaration((node) {
      if (!_extendsBaseEntity(node)) return;

      // 2. Check each field
      for (final field in node.members.whereType<FieldDeclaration>()) {
        // 3. Flag non-nullable fields without defaults
        if (field.fields.type?.question == null && !_hasDefault(field)) {
          reporter.reportErrorForNode(code, field, [...]);
        }
      }
    });
  }
}

The analyzer:

  • ✅ Detects classes extending BaseEntity
  • ✅ Checks field nullability (?)
  • ✅ Checks for default values
  • ✅ Reports violations with fix suggestions

Schema Evolution Guide #

Safe patterns this analyzer enforces:

✅ SAFE: Add optional field #

// v1
class Note {
  String title;
}

// v2
class Note {
  String title;
  List<String>? tags;  // Optional - old snapshots default to null
}
  • v1 snapshot loads: tags = null
  • v2 snapshot loads: tags = ['tag1', 'tag2']
  • Old app loads v2 snapshot: tags is ignored ✅

✅ SAFE: Add field with default #

class Note {
  String title;

  @JsonKey(defaultValue: [])
  List<String> tags;  // Default provided
}
  • Old snapshots missing tags? Use []
  • New snapshots have tags? Use the value

❌ UNSAFE: Add required field #

class Note {
  String title;
  List<String> tags;  // REQUIRED - old snapshots crash!
}

Old snapshots:

{"title": "Task", "content": "..."}
// No tags field!

Deserialization fails → Entity can't be loaded.

❌ UNSAFE: Change field type #

// v1
class Note {
  String priority;  // "high", "medium", "low"
}

// v2
class Note {
  int priority;  // BREAKING! Type mismatch
}

Deserialization fails → Type error.

✅ SAFE: Migrate field type #

class Note {
  @Deprecated('Use priorityLevel instead')
  String? priority;  // Keep old field for backward compat

  @JsonKey(defaultValue: 0)
  int priorityLevel;  // New field with safe default
}
  • Old snapshots load: priority set, priorityLevel = 0
  • New app migrates data: Convert prioritypriorityLevel
  • New snapshots only have priorityLevel

Testing #

Run tests to verify the analyzer works:

cd everything_stack_analyzer
dart pub get
dart pub run build_runner build  # Build custom lints
dart test

Or from the main project:

flutter analyze --ignore-infos

Future Enhancements #

Git-based detection #

Track schema changes across commits:

// Detects this as a breaking change
class FieldTypeChangeDetection extends DartLintRule {
  // Git integration to detect:
  // - Type changes on fields
  // - Removed required fields
  // - Non-backward-compatible migrations
}

Migration framework #

Auto-generate migrations:

// Generates migration code
@Migration(from: 1, to: 2)
class AddTagsField {
  static void migrate(Map<String, dynamic> entity) {
    entity['tags'] = [];  // Safe default
  }
}

Schema documentation #

Link violations to documentation:

See: docs/schema-evolution.md#required-fields

Troubleshooting #

Analyzer not running? #

  1. Check pubspec.yaml:

    analyzer:
      plugins:
        - custom_lint
    
  2. Clear build cache:

    flutter clean
    pub cache repair
    
  3. Rebuild:

    flutter analyze
    

Too many warnings? #

Disable specific rules in analysis_options.yaml:

linter:
  rules:
    - require_field_on_new_entity: false  # Disable if not needed

False positives? #

Suppress for specific lines:

class Note extends BaseEntity {
  // ignore: require_field_on_new_entity
  List<String> tags;  // Known-safe pattern
}

Contributing #

To add new rules:

  1. Create a new DartLintRule in lib/src/lints/
  2. Implement the run() method with AST traversal
  3. Add tests in test/
  4. Update lib/everything_stack_analyzer.dart to register the rule
  5. Document in this README

References #

0
likes
70
points
102
downloads

Publisher

unverified uploader

Weekly Downloads

Analyzer plugin for Everything Stack - enforces schema evolution rules and best practices.

Repository (GitHub)

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

analyzer, custom_lint, custom_lint_core

More

Packages that depend on everything_stack_analyzer