diff --git a/.fvm/fvm_config.json b/.fvm/fvm_config.json new file mode 100644 index 0000000..e373cf5 --- /dev/null +++ b/.fvm/fvm_config.json @@ -0,0 +1,3 @@ +{ + "flutterSdkVersion": "3.41.9" +} \ No newline at end of file diff --git a/.fvmrc b/.fvmrc new file mode 100644 index 0000000..d80cf57 --- /dev/null +++ b/.fvmrc @@ -0,0 +1,3 @@ +{ + "flutter": "3.41.9" +} \ No newline at end of file diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index b0da96b..3f35098 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -1,14 +1,25 @@ -name: Setup Dart environment and get dependencies -description: Installs Dart SDK 2.12.0 and runs `dart pub get` to fetch project dependencies +name: Setup Dart environment +description: Installs Dart SDK 3.7, activates Melos, and bootstraps the workspace runs: using: "composite" steps: - name: Install Dart - uses: dart-lang/setup-dart@v1.4 + uses: dart-lang/setup-dart@v1 with: - sdk: 2.14.0 + sdk: "3.7" - - name: Get dependencies + - name: Cache pub dependencies + uses: actions/cache@v4 + with: + path: ~/.pub-cache + key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.yaml') }} + restore-keys: ${{ runner.os }}-pub- + + - name: Activate Melos + shell: bash + run: dart pub global activate melos + + - name: Bootstrap workspace shell: bash - run: dart pub get + run: melos bootstrap diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml index f306f44..a7354bb 100644 --- a/.github/workflows/analyze.yml +++ b/.github/workflows/analyze.yml @@ -8,8 +8,8 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v6 - - name: Setup Dart + uses: actions/checkout@v4 + - name: Setup uses: ./.github/actions/setup - name: Analyze - run: dart analyze \ No newline at end of file + run: melos run analyze diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index d1d420f..36103bf 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -3,10 +3,14 @@ name: Dart CI on: pull_request: push: + branches: + - main + tags: + - "v*" concurrency: group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }} jobs: test: @@ -28,4 +32,4 @@ jobs: needs: - release name: Publish - uses: ./.github/workflows/publish.yml \ No newline at end of file + uses: ./.github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d4405de..8b042bc 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,7 +5,14 @@ on: jobs: publish: - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest permissions: id-token: write - uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1 \ No newline at end of file + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup + uses: ./.github/actions/setup + - name: Publish packages + run: melos publish --no-dry-run --yes diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 181c725..1dc73fb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,10 +5,10 @@ on: jobs: release: + if: startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest steps: - name: Create GitHub Release uses: softprops/action-gh-release@v2 - if: startsWith(github.ref, 'refs/tags/') env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 330d4bc..e4d0f5c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,8 +8,18 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v6 - - name: Setup Dart + uses: actions/checkout@v4 + - name: Setup uses: ./.github/actions/setup - - name: Run Tests - run: dart test + - name: Run tests + run: melos run test + + example-build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup + uses: ./.github/actions/setup + - name: Build example + run: melos run build diff --git a/.gitignore b/.gitignore index 1985397..e267bd9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ +# Superpowers +docs/superpowers/ + +# FVM +.fvm/flutter_sdk + # Miscellaneous *.class *.log diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4268883 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,68 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Purpose + +Test fixture library for Dart/Flutter projects. Solves the problem of creating realistic fake model instances in tests without boilerplate. Instead of hand-crafting constructors with dummy data in every test file, you define a factory once and call `makeSingle()` / `makeMany(n)` anywhere. + +Core dependency is `package:faker` (re-exported), which supplies realistic random data (names, dates, companies, etc.). + +### When to use which method + +| Need | Method | +|---|---| +| Model instance for a test | `makeSingle()` / `makeMany(n)` | +| JSON map (e.g. to test a deserializer) | `makeJsonObject()` / `makeJsonArray(n)` | +| Both model + JSON in sync (e.g. to test round-trip serialization) | `makeSingleWithJsonObject()` / `makeManyWithJsonArray(n)` | +| JSON from already-existing model instances | `makeJsonObjectFromSingle(obj)` / `makeJsonArrayFromMany(list)` | +| Edge-case variant (different fields, empty list, etc.) | define a named method returning `redefine()` / `redefineJson()` | + +## Commands + +```shell +dart test # run all tests +dart test test/foo_test.dart # run single test file +dart analyze # static analysis +``` + +## Architecture + +Pure Dart package — no Flutter dependency. Published to pub.dev as `data_fixture_dart`. + +### Layer responsibilities + +**Makers** (`lib/makers/`) — abstract interfaces only: +- `FixtureMaker` — `makeSingle()`, `makeMany(int)` +- `JsonFixtureMaker` extends above — adds JSON output methods + +**Definitions** (`lib/definitions/`) — concrete generation logic: +- `FixtureDefinition` — holds a `FixtureDefinitionBuilder` (a `(Faker, [int index]) => Model` lambda) and a `Faker` instance; `makeMany` drives the lambda via `List.generate`, passing `index` +- `JsonFixtureDefinition` — wraps a `FixtureDefinition` + a `JsonFixtureDefinitionBuilder` (`(Model, [int index]) => Map`); JSON generation always generates the model first, then maps it to JSON + +**Factories** (`lib/factories/`) — public API consumed by users: +- `FixtureFactory` — abstract; users override `definition()` returning a `FixtureDefinition`. Provides `define()` (new definition) and `redefine()` (mutate result of `definition()`) helpers +- `JsonFixtureFactory` extends `FixtureFactory` — users also override `jsonDefinition()`. Adds `defineJson()` and `redefineJson()` helpers + +**Misc** (`lib/misc/`): +- `FixtureTuple` — value object pairing `object` + `json`; returned by `makeSingleWithJsonObject()` / `makeManyWithJsonArray()` + +### Key design rules + +- `redefine()` calls `definition().definition(faker, index)` internally, then passes the result to the user's lambda — so partial overrides are cheap (just change the fields you care about). +- `redefineJson()` reuses `jsonDefinition().jsonDefinition` (the builder), so JSON shape stays the same when you only want to change the model. +- `index` is exposed to both builder lambdas so generated collections can have stable, predictable field values (e.g., sequential IDs in tests). +- Custom `Faker` instances (with seed or custom providers) can be injected via the `faker:` named parameter on `define`/`redefine`. +- The library re-exports `package:faker/faker.dart` so consumers only need one import. + +### Convention for test fixtures + +Factories are private (`_FooFixtureFactory`) and exposed via an extension method on the model: + +```dart +extension FooFixture on Foo { + static _FooFixtureFactory factory() => _FooFixtureFactory(); +} +``` + +Custom definitions (e.g., `empty()`, `withFields()`) return `FixtureDefinition` or `JsonFixtureDefinition` so callers can chain directly into `makeSingle()` / `makeJsonObject()` etc. diff --git a/README.md b/README.md deleted file mode 100644 index a0efc36..0000000 --- a/README.md +++ /dev/null @@ -1,178 +0,0 @@ -# data_fixture_dart - -![data_fixture_dart_ci](https://github.com/MyLittleSuite/data_fixture_dart/workflows/Dart%20CI/badge.svg) -[![Pub](https://img.shields.io/pub/v/data_fixture_dart.svg)](https://pub.dev/packages/data_fixture_dart) -[![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT) -[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/dwyl/esta/issues) - -Create data models easily, with no headache. - -## Usage - -### Basic -1. Create a new file to define the fixture factory for a model. -```dart -import 'package:data_fixture_dart/data_fixture_dart.dart'; - -class Company { - final String name; - final List employees; - - Company({this.name, this.employees}); -} - -extension CompanyFixture on Company { - static _CompanyFixtureFactory factory() => _CompanyFixtureFactory(); -} - -class _CompanyFixtureFactory extends FixtureFactory { - @override - FixtureDefinition definition() => define( - (faker, [int index = 0]) => Company( - name: faker.company.name(), - employees: PersonFixture.factory().makeMany(5), - ), - ); - - // If you need to override a model field, simply define a function that returns a `FixtureDefinition`. - // To redefine the default definition, you must use the `redefine` function. - FixtureDefinition empty(String name) => redefine( - (company, [int index = 0]) => Company( - name: name, - employees: [], - ), - ); -} -``` - -2. Then you can build the model by using its factory. -```dart -// Create a single object of type Company. -CompanyFixture.factory().makeSingle(); -// Create a single object of type Company with no employees. -CompanyFixture.factory().empty("EmptyCompany").make(); - -// Create 10 objects of type Company. -CompanyFixture.factory().makeMany(10); -// Create 10 objects of type Company with no employees. -CompanyFixture.factory().empty("EmptyCompany").makeMany(10); -``` - -### JSON Fixtures -A factory can create a JSON Object from a generated model. -1. First, you have to extend `JSONFixtureFactory` protocol to the model factory. -```dart -import 'package:data_fixture_dart/data_fixture_dart.dart'; - -extension CompanyFixture on Company { - static _CompanyFixtureFactory factory() => _CompanyFixtureFactory(); -} - -class _CompanyFixtureFactory extends JsonFixtureFactory { - @override - FixtureDefinition definition() => define( - (faker, [int index = 0]) => Company( - name: faker.company.name(), - employees: PersonFixture.factory().makeMany(5), - ), - ); - - // This function define the json definition, using the default definition (function `definition()`). - @override - JsonFixtureDefinition jsonDefinition() => defineJson( - (company, [int index = 0]) => { - "name": company.name, - "employees": - PersonFixture.factory().makeJsonArrayFromMany(company.employees), - }, - ); - - // If you need to generate the JSON Object of an empty company, change the return type to `JSONFixtureDefinition` - // Previously the return was `FixtureDefinition`. - JsonFixtureDefinition empty(String name) => redefineJson( - (company, [int index = 0]) => Company( - name: name, - employees: [], - ), - ); -} -``` - -2. Now you can generate the JSON Object of the model. -```dart -// Create a single JSON object of type Company. -CompanyFixture.factory().makeJsonObject(); -// Create a single JSON object of type Company with no employees. -CompanyFixture.factory().empty("EmptyCompany").makeJsonObject(); - -// Create a JSON Array of 10 objects of type Company. -CompanyFixture.factory().makeJsonArray(10) -// Create a JSON Array of 10 objects of type Company with no employees. -CompanyFixture.factory().empty("EmptyCompany").makeJsonArray(10); - -// Create a Company object with its relative JSON object. -CompanyFixture.factory().makeSingleWithJsonObject(); -// Create 10 Company object with its relative JSON objects. -CompanyFixture.factory().makeManyWithJsonArray(10); -``` - -3. With `JsonFixtureFactory` you can create a JSON from an external model object. -```dart -final company = CompanyFixture.factory.makeSingle(); -final JSONObject = CompanyFixture.factory.makeJsonObjectFromSingle(from: company); - -final companies = CompanyFixture.factory.makeMany(3); -final JSONArray = CompanyFixture.factory.makeJsonArrayFromMany(from: companies); -``` - -### Using a custom Faker instance - -Sometimes you want your `Faker` to have maybe a custom `seed` or custom `provider`. -In that case you can simply pass a custom `Faker` instance to either `define` or `redefine` -```dart -import 'package:data_fixture_dart/data_fixture_dart.dart'; - -extension NewsArticleFixture on NewsArticle { - static _NewsArticleFactory factory() => _NewsArticleFactory(); -} - -class _NewsArticleFixtureFactory extends FixtureFactory { - @override - FixtureDefinition definition() => define( - (Faker faker, [int index = 0]) => NewsArticle( - title: faker.lorem.sentence(), - content: faker.lorem.sentences(3).join(' '), - ), - faker: Faker( - seed: Random().nextInt(1234567890), - provider: FakerDataProvider( - loremDataProvider: MyCustomLoremDataProvider(), - ), - ), - ); - - FixtureDefinition noContent() => redefine( - (newsArticle, [int index = 0]) => NewsArticle( - title: faker.lorem.sentence(), - content: null, - ), - faker: Faker( - seed: Random().nextInt(9876543210), - provider: FakerDataProvider( - loremDataProvider: MyOtherCustomLoremDataProvider(), - ), - ), - ); -} -``` - -## Contributing -data_fixture_dart is an open source project, so feel free to contribute. -You can open an issue for problems or suggestions, and you can propose your own fixes by opening a pull request with the changes. - -## Testing -In order to test the package run this command - -```shell -dart test -``` \ No newline at end of file diff --git a/docs/migration/manual-to-generator.md b/docs/migration/manual-to-generator.md new file mode 100644 index 0000000..4e74cf0 --- /dev/null +++ b/docs/migration/manual-to-generator.md @@ -0,0 +1,203 @@ +# Migrating from manual factories to the generator + +The generator produces the same `FixtureFactory` / `JsonFixtureFactory` boilerplate you'd write by hand. Manual factories remain fully supported — you can adopt the generator incrementally, one model at a time. + +## When to use the generator + +| Use the generator | Stick with manual | +|---|---| +| Standard model fields that map to `FakerType` | Complex builder logic (computed fields, conditional branching) | +| Traits with literal overrides | Seeded `Faker` for reproducible tests | +| Nested models with standard fields (auto-discovered) | Nested factories with cross-model logic beyond `makeSingle()` | +| Reducing boilerplate in large test suites | | + +## Setup + +**1. Add packages to `pubspec.yaml`:** + +```yaml +dependencies: + data_fixture_dart_annotations: ^1.0.0 + +dev_dependencies: + data_fixture_dart_generator: ^1.0.0 + build_runner: ^2.4.0 +``` + +**2. Enable the builder in `build.yaml`** (create at project root if missing): + +```yaml +targets: + $default: + builders: + data_fixture_dart_generator|fixture_generator: + enabled: true +``` + +## Before and after + +### Model without JSON + +**Before — hand-written factory:** + +```dart +extension DogFixture on Dog { + static _DogFixtureFactory factory() => _DogFixtureFactory(); +} + +class _DogFixtureFactory extends FixtureFactory { + @override + FixtureDefinition definition() => define( + (faker, [int index = 0]) => Dog( + id: index, + name: faker.person.name(), + age: faker.randomGenerator.integer(100), + ), + ); +} +``` + +**After — annotation:** + +```dart +// fixtures.dart +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:my_app/models/dog.dart'; + +@FixtureFor(Dog) +void fixtures() {} +``` + +Run `dart run build_runner build`. The generator produces `fixtures.fixture.dart` with an equivalent `_$DogFixtureFactory` and a `DogFixture` extension. + +### Model with JSON + +**Before:** + +```dart +class _UserFixtureFactory extends JsonFixtureFactory { + @override + FixtureDefinition definition() => define( + (faker, [int index = 0]) => User( + id: index, + name: faker.person.name(), + email: faker.internet.email(), + ), + ); + + @override + JsonFixtureDefinition jsonDefinition() => defineJson( + (user, [int index = 0]) => user.toJson(), + ); +} +``` + +**After:** + +```dart +@FixtureFor(User, hasJson: true) +void fixtures() {} +``` + +`hasJson: true` tells the generator to extend `JsonFixtureFactory` and call `toJson()` in `jsonDefinition`. Your model must expose a `toJson()` method (e.g., via `json_serializable`). + +## Migrating traits + +**Before — hand-written trait method:** + +```dart +FixtureDefinition old() => redefine( + (dog, [int index = 0]) => Dog( + id: dog.id, + name: dog.name, + age: 20, + ), + ); +``` + +**After — annotation trait:** + +```dart +@FixtureFor(Dog, traits: { + 'old': {#age: FixtureTraitValue.literal(20)}, +}) +void fixtures() {} +``` + +For fields that should use a faker method rather than a literal: + +```dart +@FixtureFor(User, traits: { + 'withRandomEmail': {#email: FixtureTraitValue.faker(FakerType.internetEmail)}, +}) +void fixtures() {} +``` + +## Overriding field faker types + +By default the generator infers a `FakerType` from the parameter name using fuzzy matching. Override any field explicitly: + +```dart +@FixtureFor( + Product, + fields: { + #title: FakerType.loremSentence, + #price: FakerType.randomDouble, + }, +) +void fixtures() {} +``` + +## Multiple models in one file + +All `@FixtureFor` annotations on a single function are generated together: + +```dart +@FixtureFor(Dog, traits: {'old': {#age: FixtureTraitValue.literal(20)}}) +@FixtureFor(User, hasJson: true) +@FixtureFor(Product, fields: {#price: FakerType.randomDouble}) +void fixtures() {} +``` + +## Nested models + +Declare only the top-level model. The generator auto-discovers nested custom types and produces a base factory for each. + +**Before — hand-written nested factories:** + +```dart +class _OwnerFixtureFactory extends FixtureFactory { + @override + FixtureDefinition definition() => define( + (faker, [int index = 0]) => Owner( + id: index, + name: faker.person.name(), + user: UserFixture.factory().makeSingle(), + dogs: DogFixture.factory().makeMany(3), + ), + ); +} +``` + +**After — one annotation, nested factories auto-generated:** + +```dart +@FixtureFor(Owner) +void fixtures() {} +``` + +The generator produces factory classes for `Owner`, `User`, and `Dog`. `List` defaults to `[]` in the generated definition — override via a trait or manual factory if you need a populated list. + +If a nested type already has its own `@FixtureFor`, the explicit declaration takes precedence and no duplicate is generated. + +## Running the generator + +```shell +# One-off build +dart run build_runner build + +# Watch mode during development +dart run build_runner watch +``` + +Generated files end in `.fixture.dart` and contain a `// GENERATED CODE - DO NOT MODIFY BY HAND` header. Commit them alongside your source files. diff --git a/docs/migration/v3-to-v4.md b/docs/migration/v3-to-v4.md new file mode 100644 index 0000000..eb53580 --- /dev/null +++ b/docs/migration/v3-to-v4.md @@ -0,0 +1,54 @@ +# Migrating from v3 to v4 + +## Overview + +v4 is a **Dart 3 release**. The core `data_fixture_dart` API is unchanged — existing factory classes require no modifications. The primary changes are the SDK constraint bump and two new optional packages. + +## 1. Update the SDK constraint + +```yaml +# pubspec.yaml — before +environment: + sdk: ">=2.12.0 <3.0.0" + +# pubspec.yaml — after +environment: + sdk: ">=3.0.0 <4.0.0" +``` + +## 2. Update the package version + +```yaml +dependencies: + data_fixture_dart: ^4.0.0 +``` + +Run: + +```shell +dart pub get +``` + +## 3. No API changes required + +All existing `FixtureFactory` and `JsonFixtureFactory` subclasses continue to work without modification. The following APIs are unchanged: + +- `FixtureFactory` / `JsonFixtureFactory` +- `FixtureDefinition` / `JsonFixtureDefinition` +- `define()` / `redefine()` / `defineJson()` / `redefineJson()` +- `makeSingle()` / `makeMany(n)` / `makeJsonObject()` / `makeJsonArray(n)` +- `makeSingleWithJsonObject()` / `makeManyWithJsonArray(n)` +- `makeJsonObjectFromSingle()` / `makeJsonArrayFromMany()` +- `FixtureTuple` +- Custom `Faker` injection via `faker:` named parameter + +## 4. New optional packages + +v4 introduces two new packages for annotation-driven code generation. Adoption is **optional** — you can continue using hand-written factories alongside generated ones. + +| Package | Purpose | +|---|---| +| `data_fixture_dart_annotations` | `@FixtureFor` annotation, `FakerType` enum, traits | +| `data_fixture_dart_generator` | `build_runner` generator that reads `@FixtureFor` and produces factory boilerplate | + +See the [manual-to-generator migration guide](manual-to-generator.md) if you want to adopt the generator. diff --git a/example/build.yaml b/example/build.yaml new file mode 100644 index 0000000..dd64849 --- /dev/null +++ b/example/build.yaml @@ -0,0 +1,5 @@ +targets: + $default: + builders: + data_fixture_dart_generator|fixture_generator: + enabled: true diff --git a/example/lib/fixtures.dart b/example/lib/fixtures.dart new file mode 100644 index 0000000..9794f4c --- /dev/null +++ b/example/lib/fixtures.dart @@ -0,0 +1,13 @@ +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:example/models/dog.dart'; +import 'package:example/models/owner.dart'; +import 'package:example/models/user.dart'; + +@FixtureFor(Dog, traits: { + 'old': {#age: FixtureTraitValue.literal(20)}, +}) +@FixtureFor(User, hasJson: true, traits: { + 'verified': {#isVerified: FixtureTraitValue.literal(true)}, +}) +@FixtureFor(Owner) +void fixtures() {} diff --git a/example/lib/fixtures.fixture.dart b/example/lib/fixtures.fixture.dart new file mode 100644 index 0000000..d234239 --- /dev/null +++ b/example/lib/fixtures.fixture.dart @@ -0,0 +1,81 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ************************************************************************** +// FixtureGenerator +// ************************************************************************** + +// ignore_for_file: type=lint + +import 'package:data_fixture_dart/data_fixture_dart.dart'; +import 'package:example/models/dog.dart'; +import 'package:example/models/user.dart'; +import 'package:example/models/owner.dart'; + +class _$DogFixtureFactory extends FixtureFactory { + @override + FixtureDefinition definition() => define( + (faker, [int index = 0]) => Dog( + id: index, + name: faker.person.name(), + age: faker.randomGenerator.integer(100), + ), + ); + + FixtureDefinition old() => redefine( + (dog, [int index = 0]) => Dog( + id: dog.id, + name: dog.name, + age: 20, + ), + ); +} + +extension DogFixture on Dog { + static _$DogFixtureFactory factory() => _$DogFixtureFactory(); +} + +class _$UserFixtureFactory extends JsonFixtureFactory { + @override + FixtureDefinition definition() => define( + (faker, [int index = 0]) => User( + id: index, + name: faker.person.name(), + email: faker.internet.email(), + isVerified: faker.randomGenerator.boolean(), + ), + ); + + @override + JsonFixtureDefinition jsonDefinition() => defineJson( + (user, [int index = 0]) => user.toJson(), + ); + + JsonFixtureDefinition verified() => redefineJson( + (user, [int index = 0]) => User( + id: user.id, + name: user.name, + email: user.email, + isVerified: true, + ), + ); +} + +extension UserFixture on User { + static _$UserFixtureFactory factory() => _$UserFixtureFactory(); +} + +class _$OwnerFixtureFactory extends FixtureFactory { + @override + FixtureDefinition definition() => define( + (faker, [int index = 0]) => Owner( + id: index, + name: faker.person.name(), + user: UserFixture.factory().makeSingle(), + dogs: [], + ), + ); +} + +extension OwnerFixture on Owner { + static _$OwnerFixtureFactory factory() => _$OwnerFixtureFactory(); +} diff --git a/example/lib/models/dog.dart b/example/lib/models/dog.dart new file mode 100644 index 0000000..d4a201c --- /dev/null +++ b/example/lib/models/dog.dart @@ -0,0 +1,7 @@ +class Dog { + final int id; + final String name; + final int age; + + Dog({required this.id, required this.name, required this.age}); +} diff --git a/example/lib/models/owner.dart b/example/lib/models/owner.dart new file mode 100644 index 0000000..a4a413d --- /dev/null +++ b/example/lib/models/owner.dart @@ -0,0 +1,16 @@ +import 'package:example/models/dog.dart'; +import 'package:example/models/user.dart'; + +class Owner { + final int id; + final String name; + final User user; + final List dogs; + + Owner({ + required this.id, + required this.name, + required this.user, + required this.dogs, + }); +} diff --git a/example/lib/models/user.dart b/example/lib/models/user.dart new file mode 100644 index 0000000..4d7f52e --- /dev/null +++ b/example/lib/models/user.dart @@ -0,0 +1,16 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'user.freezed.dart'; +part 'user.g.dart'; + +@freezed +class User with _$User { + const factory User({ + required int id, + required String name, + required String email, + required bool isVerified, + }) = _User; + + factory User.fromJson(Map json) => _$UserFromJson(json); +} diff --git a/example/lib/models/user.freezed.dart b/example/lib/models/user.freezed.dart new file mode 100644 index 0000000..8e4e555 --- /dev/null +++ b/example/lib/models/user.freezed.dart @@ -0,0 +1,203 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'user.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +User _$UserFromJson(Map json) { + return _User.fromJson(json); +} + +/// @nodoc +mixin _$User { + int get id => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + String get email => throw _privateConstructorUsedError; + bool get isVerified => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $UserCopyWith get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $UserCopyWith<$Res> { + factory $UserCopyWith(User value, $Res Function(User) then) = + _$UserCopyWithImpl<$Res, User>; + @useResult + $Res call({int id, String name, String email, bool isVerified}); +} + +/// @nodoc +class _$UserCopyWithImpl<$Res, $Val extends User> + implements $UserCopyWith<$Res> { + _$UserCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? name = null, + Object? email = null, + Object? isVerified = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + email: null == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String, + isVerified: null == isVerified + ? _value.isVerified + : isVerified // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$UserImplCopyWith<$Res> implements $UserCopyWith<$Res> { + factory _$$UserImplCopyWith( + _$UserImpl value, $Res Function(_$UserImpl) then) = + __$$UserImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({int id, String name, String email, bool isVerified}); +} + +/// @nodoc +class __$$UserImplCopyWithImpl<$Res> + extends _$UserCopyWithImpl<$Res, _$UserImpl> + implements _$$UserImplCopyWith<$Res> { + __$$UserImplCopyWithImpl(_$UserImpl _value, $Res Function(_$UserImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? name = null, + Object? email = null, + Object? isVerified = null, + }) { + return _then(_$UserImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + email: null == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String, + isVerified: null == isVerified + ? _value.isVerified + : isVerified // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$UserImpl implements _User { + const _$UserImpl( + {required this.id, + required this.name, + required this.email, + required this.isVerified}); + + factory _$UserImpl.fromJson(Map json) => + _$$UserImplFromJson(json); + + @override + final int id; + @override + final String name; + @override + final String email; + @override + final bool isVerified; + + @override + String toString() { + return 'User(id: $id, name: $name, email: $email, isVerified: $isVerified)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$UserImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.name, name) || other.name == name) && + (identical(other.email, email) || other.email == email) && + (identical(other.isVerified, isVerified) || + other.isVerified == isVerified)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, id, name, email, isVerified); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$UserImplCopyWith<_$UserImpl> get copyWith => + __$$UserImplCopyWithImpl<_$UserImpl>(this, _$identity); + + @override + Map toJson() { + return _$$UserImplToJson( + this, + ); + } +} + +abstract class _User implements User { + const factory _User( + {required final int id, + required final String name, + required final String email, + required final bool isVerified}) = _$UserImpl; + + factory _User.fromJson(Map json) = _$UserImpl.fromJson; + + @override + int get id; + @override + String get name; + @override + String get email; + @override + bool get isVerified; + @override + @JsonKey(ignore: true) + _$$UserImplCopyWith<_$UserImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/example/lib/models/user.g.dart b/example/lib/models/user.g.dart new file mode 100644 index 0000000..b2f8898 --- /dev/null +++ b/example/lib/models/user.g.dart @@ -0,0 +1,22 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$UserImpl _$$UserImplFromJson(Map json) => _$UserImpl( + id: (json['id'] as num).toInt(), + name: json['name'] as String, + email: json['email'] as String, + isVerified: json['isVerified'] as bool, + ); + +Map _$$UserImplToJson(_$UserImpl instance) => + { + 'id': instance.id, + 'name': instance.name, + 'email': instance.email, + 'isVerified': instance.isVerified, + }; diff --git a/example/pubspec.lock b/example/pubspec.lock new file mode 100644 index 0000000..f756b8f --- /dev/null +++ b/example/pubspec.lock @@ -0,0 +1,594 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + url: "https://pub.dev" + source: hosted + version: "67.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + url: "https://pub.dev" + source: hosted + version: "6.4.1" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.dev" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 + url: "https://pub.dev" + source: hosted + version: "4.1.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d" + url: "https://pub.dev" + source: hosted + version: "2.4.13" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 + url: "https://pub.dev" + source: hosted + version: "7.3.2" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "34e4067d30ce212937df995f03b69992eea683539ceeac7f679a1f1eba055b56" + url: "https://pub.dev" + source: hosted + version: "8.12.6" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" + url: "https://pub.dev" + source: hosted + version: "4.11.1" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" + url: "https://pub.dev" + source: hosted + version: "1.15.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + url: "https://pub.dev" + source: hosted + version: "2.3.6" + data_fixture_dart: + dependency: "direct main" + description: + path: "../packages/data_fixture_dart" + relative: true + source: path + version: "4.0.0" + data_fixture_dart_annotations: + dependency: "direct main" + description: + path: "../packages/data_fixture_dart_annotations" + relative: true + source: path + version: "1.0.0" + data_fixture_dart_generator: + dependency: "direct dev" + description: + path: "../packages/data_fixture_dart_generator" + relative: true + source: path + version: "1.0.0" + faker: + dependency: transitive + description: + name: faker + sha256: "544c34e9e1d322824156d5a8d451bc1bb778263b892aded24ec7ba77b0706624" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + freezed: + dependency: "direct dev" + description: + name: freezed + sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1 + url: "https://pub.dev" + source: hosted + version: "2.5.2" + freezed_annotation: + dependency: "direct main" + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + js: + dependency: transitive + description: + name: js + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + json_annotation: + dependency: "direct main" + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b + url: "https://pub.dev" + source: hosted + version: "6.8.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + meta: + dependency: transitive + description: + name: meta + sha256: df0c643f44ad098eb37988027a8e2b2b5a031fd3977f06bbfd3a76637e8df739 + url: "https://pub.dev" + source: hosted + version: "1.18.2" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + pool: + dependency: transitive + description: + name: pool + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + url: "https://pub.dev" + source: hosted + version: "1.5.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c" + url: "https://pub.dev" + source: hosted + version: "1.3.5" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + url: "https://pub.dev" + source: hosted + version: "0.10.13" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test: + dependency: "direct dev" + description: + name: test + sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" + url: "https://pub.dev" + source: hosted + version: "1.26.3" + test_api: + dependency: transitive + description: + name: test_api + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + url: "https://pub.dev" + source: hosted + version: "0.7.7" + test_core: + dependency: transitive + description: + name: test_core + sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" + url: "https://pub.dev" + source: hosted + version: "0.6.12" + timing: + dependency: transitive + description: + name: timing + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" + url: "https://pub.dev" + source: hosted + version: "15.2.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" +sdks: + dart: ">=3.7.0 <4.0.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml new file mode 100644 index 0000000..2af896c --- /dev/null +++ b/example/pubspec.yaml @@ -0,0 +1,23 @@ +name: example +description: Example project for data_fixture_dart generator. +version: 1.0.0 +publish_to: none + +environment: + sdk: ">=3.7.0 <4.0.0" + +dependencies: + data_fixture_dart: + path: ../packages/data_fixture_dart + data_fixture_dart_annotations: + path: ../packages/data_fixture_dart_annotations + freezed_annotation: ^2.4.0 + json_annotation: ^4.9.0 + +dev_dependencies: + data_fixture_dart_generator: + path: ../packages/data_fixture_dart_generator + build_runner: ^2.4.0 + freezed: ^2.5.0 + json_serializable: ^6.8.0 + test: ^1.25.0 diff --git a/example/test/example_test.dart b/example/test/example_test.dart new file mode 100644 index 0000000..cab1e13 --- /dev/null +++ b/example/test/example_test.dart @@ -0,0 +1,74 @@ +import 'package:example/models/dog.dart'; +import 'package:example/models/owner.dart'; +import 'package:example/models/user.dart'; +import 'package:test/test.dart'; + +import '../lib/fixtures.fixture.dart'; + +void main() { + group('Dog', () { + test('makeSingle', () { + final dog = DogFixture.factory().makeSingle(); + expect(dog, isA()); + }); + + test('makeMany', () { + final dogs = DogFixture.factory().makeMany(3); + expect(dogs.length, 3); + }); + + test('old trait sets age to 20', () { + final dog = DogFixture.factory().old().makeSingle(); + expect(dog.age, 20); + }); + }); + + group('User', () { + test('makeSingle', () { + final user = UserFixture.factory().makeSingle(); + expect(user, isA()); + }); + + test('makeJsonObject', () { + final json = UserFixture.factory().makeJsonObject(); + expect(json, isA>()); + expect(json.containsKey('name'), isTrue); + }); + + test('verified trait sets isVerified to true', () { + final tuple = UserFixture.factory().verified().makeSingleWithJsonObject(); + expect(tuple.object.isVerified, isTrue); + expect(tuple.json['isVerified'], isTrue); + }); + }); + + group('Owner (nested data)', () { + test('makeSingle contains nested User', () { + final owner = OwnerFixture.factory().makeSingle(); + expect(owner, isA()); + expect(owner.user, isA()); + expect(owner.user.name, isNotEmpty); + expect(owner.user.email, isNotEmpty); + }); + + test('makeSingle dogs defaults to empty list', () { + final owner = OwnerFixture.factory().makeSingle(); + expect(owner.dogs, isEmpty); + }); + + test('makeMany produces correct count with distinct ids', () { + final owners = OwnerFixture.factory().makeMany(3); + expect(owners.length, 3); + expect(owners.map((o) => o.id).toList(), [0, 1, 2]); + }); + + test('each owner has independently generated nested User', () { + final owners = OwnerFixture.factory().makeMany(2); + // both have valid User instances + for (final owner in owners) { + expect(owner.user, isA()); + expect(owner.user.name, isNotEmpty); + } + }); + }); +} diff --git a/example/test/fixtures.dart b/example/test/fixtures.dart new file mode 100644 index 0000000..aa29e16 --- /dev/null +++ b/example/test/fixtures.dart @@ -0,0 +1,5 @@ +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:example/models/dog.dart'; + +@FixtureFor(Dog, fields: {#name: FakerType.personFirstName}) +void testFixtures() {} diff --git a/example/test/fixtures.fixture.dart b/example/test/fixtures.fixture.dart new file mode 100644 index 0000000..8f129fa --- /dev/null +++ b/example/test/fixtures.fixture.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ************************************************************************** +// FixtureGenerator +// ************************************************************************** + +// ignore_for_file: type=lint + +import 'package:data_fixture_dart/data_fixture_dart.dart'; +import 'package:example/models/dog.dart'; + +class _$DogFixtureFactory extends FixtureFactory { + @override + FixtureDefinition definition() => define( + (faker, [int index = 0]) => Dog( + id: index, + name: faker.person.firstName(), + age: faker.randomGenerator.integer(100), + ), + ); +} + +extension DogFixture on Dog { + static _$DogFixtureFactory factory() => _$DogFixtureFactory(); +} diff --git a/melos.yaml b/melos.yaml new file mode 100644 index 0000000..0b08888 --- /dev/null +++ b/melos.yaml @@ -0,0 +1,39 @@ +name: data_fixture_dart_workspace + +packages: + - packages/* + - example + +command: + bootstrap: + usePubspecOverrides: true + +scripts: + analyze: + run: dart analyze + exec: + concurrency: 5 + description: Run dart analyze in all packages + + test: + run: dart test + exec: + concurrency: 1 + failFast: true + packageFilters: + dirExists: test + description: Run dart test in packages that have tests + + build: + run: dart run build_runner build --delete-conflicting-outputs + exec: + concurrency: 1 + packageFilters: + scope: example + description: Run build_runner in example + + format: + run: dart format --set-exit-if-changed . + exec: + concurrency: 5 + description: Check dart formatting in all packages diff --git a/CHANGELOG.md b/packages/data_fixture_dart/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to packages/data_fixture_dart/CHANGELOG.md diff --git a/packages/data_fixture_dart/README.md b/packages/data_fixture_dart/README.md new file mode 100644 index 0000000..b29bab4 --- /dev/null +++ b/packages/data_fixture_dart/README.md @@ -0,0 +1,291 @@ +# data_fixture_dart + +![data_fixture_dart_ci](https://github.com/MyLittleSuite/data_fixture_dart/workflows/Dart%20CI/badge.svg) +[![Pub](https://img.shields.io/pub/v/data_fixture_dart.svg)](https://pub.dev/packages/data_fixture_dart) +[![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT) +[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/dwyl/esta/issues) + +Create fake model instances for tests without boilerplate. Define a factory once, call `makeSingle()` or `makeMany(n)` anywhere. + +## Packages + +| Package | pub.dev | Description | +|---|---|---| +| `data_fixture_dart` | [![Pub](https://img.shields.io/pub/v/data_fixture_dart.svg)](https://pub.dev/packages/data_fixture_dart) | Core library — factory base classes and `faker` re-export | +| `data_fixture_dart_annotations` | [![Pub](https://img.shields.io/pub/v/data_fixture_dart_annotations.svg)](https://pub.dev/packages/data_fixture_dart_annotations) | Annotations for code generation (`@FixtureFor`, `FakerType`, traits) | +| `data_fixture_dart_generator` | [![Pub](https://img.shields.io/pub/v/data_fixture_dart_generator.svg)](https://pub.dev/packages/data_fixture_dart_generator) | `build_runner` generator — produces factory boilerplate from annotations | + +--- + +## Manual approach + +Write factory classes by hand. Full control — use arbitrary Dart logic in builders. + +### Basic + +```dart +import 'package:data_fixture_dart/data_fixture_dart.dart'; + +class Company { + final String name; + final List employees; + + Company({required this.name, required this.employees}); +} + +extension CompanyFixture on Company { + static _CompanyFixtureFactory factory() => _CompanyFixtureFactory(); +} + +class _CompanyFixtureFactory extends FixtureFactory { + @override + FixtureDefinition definition() => define( + (faker, [int index = 0]) => Company( + name: faker.company.name(), + employees: PersonFixture.factory().makeMany(5), + ), + ); + + FixtureDefinition empty(String name) => redefine( + (company, [int index = 0]) => Company( + name: name, + employees: [], + ), + ); +} +``` + +```dart +// Single instance +Company company = CompanyFixture.factory().makeSingle(); + +// Custom variant +Company empty = CompanyFixture.factory().empty('ACME').make(); + +// List of 10 +List companies = CompanyFixture.factory().makeMany(10); +``` + +### JSON fixtures + +Extend `JsonFixtureFactory` and override `jsonDefinition()`. + +```dart +extension CompanyFixture on Company { + static _CompanyFixtureFactory factory() => _CompanyFixtureFactory(); +} + +class _CompanyFixtureFactory extends JsonFixtureFactory { + @override + FixtureDefinition definition() => define( + (faker, [int index = 0]) => Company( + name: faker.company.name(), + employees: PersonFixture.factory().makeMany(5), + ), + ); + + @override + JsonFixtureDefinition jsonDefinition() => defineJson( + (company, [int index = 0]) => { + 'name': company.name, + 'employees': PersonFixture.factory().makeJsonArrayFromMany(company.employees), + }, + ); + + JsonFixtureDefinition empty(String name) => redefineJson( + (company, [int index = 0]) => Company( + name: name, + employees: [], + ), + ); +} +``` + +```dart +// JSON object +Map json = CompanyFixture.factory().makeJsonObject(); + +// JSON array of 10 +List> jsonArray = CompanyFixture.factory().makeJsonArray(10); + +// Paired model + JSON +FixtureTuple tuple = CompanyFixture.factory().makeSingleWithJsonObject(); + +// JSON from an existing instance +final company = CompanyFixture.factory().makeSingle(); +final json = CompanyFixture.factory().makeJsonObjectFromSingle(company); +``` + +### Custom Faker instance + +Pass a seeded or custom-provider `Faker` to `define`/`redefine`. + +```dart +class _NewsArticleFixtureFactory extends FixtureFactory { + @override + FixtureDefinition definition() => define( + (faker, [int index = 0]) => NewsArticle( + title: faker.lorem.sentence(), + content: faker.lorem.sentences(3).join(' '), + ), + faker: Faker( + seed: 42, + provider: FakerDataProvider( + loremDataProvider: MyCustomLoremDataProvider(), + ), + ), + ); +} +``` + +--- + +## Generator approach + +Let `build_runner` generate factory boilerplate from `@FixtureFor` annotations. Best for models with standard fields where `FakerType` mappings cover your needs. + +### Setup + +**1. Add dependencies to `pubspec.yaml`:** + +```yaml +dependencies: + data_fixture_dart: ^4.0.0 + data_fixture_dart_annotations: ^1.0.0 + +dev_dependencies: + data_fixture_dart_generator: ^1.0.0 + build_runner: ^2.4.0 +``` + +**2. Enable the builder in `build.yaml`** (create the file at the project root if it doesn't exist): + +```yaml +targets: + $default: + builders: + data_fixture_dart_generator|fixture_generator: + enabled: true +``` + +### Annotate models + +Create a Dart file (e.g., `test/fixtures.dart`) and annotate a `void` function with one `@FixtureFor` per model: + +```dart +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:my_app/models/dog.dart'; +import 'package:my_app/models/user.dart'; + +@FixtureFor(Dog) +@FixtureFor(User, hasJson: true) +void fixtures() {} +``` + +**3. Run the generator:** + +```shell +dart run build_runner build +``` + +This produces a `fixtures.fixture.dart` file alongside the annotated file. + +### Nested models + +Only declare top-level models. The generator automatically discovers nested custom types and produces a base `FixtureFactory` for each one — no extra annotations needed. + +```dart +// Only declare Owner. Dog and Address are auto-discovered. +@FixtureFor(Owner) +void fixtures() {} +``` + +Generated output includes factory classes for `Owner`, `Dog`, and `Address`. The nested factories use sensible defaults (same field resolution rules as explicit declarations). Lists of custom types default to `[]` in the generated definition. + +**`hasJson` propagation**: if the root has `hasJson: true`, nested types that expose `toJson()` automatically get `JsonFixtureFactory`. Nested types without `toJson()` fall back to `FixtureFactory`. + +**Explicit wins**: if a nested type has its own `@FixtureFor`, the explicit declaration is used and no base factory is auto-generated for it. + +Discovery is recursive — if `Owner` → `Address` → `Country`, all three get factories from a single `@FixtureFor(Owner)`. + +### @FixtureFor options + +| Parameter | Type | Description | +|---|---|---| +| `modelType` | `Type` | The model class to generate a factory for | +| `constructor` | `String?` | Named constructor to use (default: unnamed) | +| `hasJson` | `bool?` | Generate `JsonFixtureFactory` with `toJson()` | +| `fields` | `Map` | Override `FakerType` per constructor parameter | +| `traits` | `Map>` | Named variants with overridden field values | + +### FakerType values + +| Enum value | Generated data | +|---|---| +| `personFirstName` | `faker.person.firstName()` | +| `personLastName` | `faker.person.lastName()` | +| `personName` | `faker.person.name()` | +| `internetEmail` | `faker.internet.email()` | +| `internetUrl` | `faker.internet.httpsUrl()` | +| `dateDateTime` | `faker.date.dateTime()` | +| `loremWord` | `faker.lorem.word()` | +| `loremSentence` | `faker.lorem.sentence()` | +| `randomInt` | `faker.randomGenerator.integer(100)` | +| `randomDouble` | `faker.randomGenerator.decimal()` | +| `addressCity` | `faker.address.city()` | +| `addressCountry` | `faker.address.country()` | +| `phoneNumber` | `faker.phoneNumber.us()` | +| `companyName` | `faker.company.name()` | + +### Traits + +Traits generate named variant methods on the factory. Use `FakerType` or literal values via `FixtureTraitValue`: + +```dart +@FixtureFor(Dog, traits: { + 'old': {#age: FixtureTraitValue.literal(20)}, +}) +@FixtureFor(User, hasJson: true, traits: { + 'verified': {#isVerified: FixtureTraitValue.literal(true)}, + 'withCustomEmail': {#email: FixtureTraitValue.faker(FakerType.internetEmail)}, +}) +void fixtures() {} +``` + +Generated usage: + +```dart +// Standard factory +Dog dog = DogFixture.factory().makeSingle(); + +// Trait variant +Dog oldDog = DogFixture.factory().old().makeSingle(); + +// JSON with trait +Map json = UserFixture.factory().verified().makeJsonObject(); +``` + +--- + +## When to use which approach + +| Situation | Approach | +|---|---| +| Standard model, fields map to `FakerType` | Generator | +| Nested models with standard fields | Generator (auto-discovered) | +| Complex builders (computed fields, conditional branching, external state) | Manual | +| Need a seeded `Faker` for reproducible tests | Manual | +| Nested factories with cross-model logic beyond simple `makeSingle()` | Manual | +| Incremental adoption — adding generator to an existing project | Both (mix freely) | + +--- + +## Contributing + +`data_fixture_dart` is an open source project. Open an issue for problems or suggestions, or propose fixes via pull request. + +## Testing + +```shell +dart test +``` diff --git a/lib/data_fixture_dart.dart b/packages/data_fixture_dart/lib/data_fixture_dart.dart similarity index 100% rename from lib/data_fixture_dart.dart rename to packages/data_fixture_dart/lib/data_fixture_dart.dart diff --git a/lib/definitions/fixture_definition.dart b/packages/data_fixture_dart/lib/definitions/fixture_definition.dart similarity index 100% rename from lib/definitions/fixture_definition.dart rename to packages/data_fixture_dart/lib/definitions/fixture_definition.dart diff --git a/lib/definitions/json_fixture_definition.dart b/packages/data_fixture_dart/lib/definitions/json_fixture_definition.dart similarity index 100% rename from lib/definitions/json_fixture_definition.dart rename to packages/data_fixture_dart/lib/definitions/json_fixture_definition.dart diff --git a/lib/factories/fixture_factory.dart b/packages/data_fixture_dart/lib/factories/fixture_factory.dart similarity index 100% rename from lib/factories/fixture_factory.dart rename to packages/data_fixture_dart/lib/factories/fixture_factory.dart diff --git a/lib/factories/json_fixture_factory.dart b/packages/data_fixture_dart/lib/factories/json_fixture_factory.dart similarity index 100% rename from lib/factories/json_fixture_factory.dart rename to packages/data_fixture_dart/lib/factories/json_fixture_factory.dart diff --git a/lib/makers/fixture_maker.dart b/packages/data_fixture_dart/lib/makers/fixture_maker.dart similarity index 100% rename from lib/makers/fixture_maker.dart rename to packages/data_fixture_dart/lib/makers/fixture_maker.dart diff --git a/lib/makers/json_fixture_maker.dart b/packages/data_fixture_dart/lib/makers/json_fixture_maker.dart similarity index 100% rename from lib/makers/json_fixture_maker.dart rename to packages/data_fixture_dart/lib/makers/json_fixture_maker.dart diff --git a/lib/misc/fixture_tuple.dart b/packages/data_fixture_dart/lib/misc/fixture_tuple.dart similarity index 100% rename from lib/misc/fixture_tuple.dart rename to packages/data_fixture_dart/lib/misc/fixture_tuple.dart diff --git a/packages/data_fixture_dart/pubspec.lock b/packages/data_fixture_dart/pubspec.lock new file mode 100644 index 0000000..4bb1982 --- /dev/null +++ b/packages/data_fixture_dart/pubspec.lock @@ -0,0 +1,389 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: f0bb5d1648339c8308cc0b9838d8456b3cfe5c91f9dc1a735b4d003269e5da9a + url: "https://pub.dev" + source: hosted + version: "88.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "0b7b9c329d2879f8f05d6c05b32ee9ec025f39b077864bdb5ac9a7b63418a98f" + url: "https://pub.dev" + source: hosted + version: "8.1.1" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.dev" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" + url: "https://pub.dev" + source: hosted + version: "1.15.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + faker: + dependency: "direct main" + description: + name: faker + sha256: "544c34e9e1d322824156d5a8d451bc1bb778263b892aded24ec7ba77b0706624" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + url: "https://pub.dev" + source: hosted + version: "0.12.19" + meta: + dependency: transitive + description: + name: meta + sha256: df0c643f44ad098eb37988027a8e2b2b5a031fd3977f06bbfd3a76637e8df739 + url: "https://pub.dev" + source: hosted + version: "1.18.2" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + pool: + dependency: transitive + description: + name: pool + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + url: "https://pub.dev" + source: hosted + version: "1.5.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + url: "https://pub.dev" + source: hosted + version: "0.10.13" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test: + dependency: "direct dev" + description: + name: test + sha256: "8d9ceddbab833f180fbefed08afa76d7c03513dfdba87ffcec2718b02bbcbf20" + url: "https://pub.dev" + source: hosted + version: "1.31.0" + test_api: + dependency: transitive + description: + name: test_api + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + url: "https://pub.dev" + source: hosted + version: "0.7.11" + test_core: + dependency: transitive + description: + name: test_core + sha256: "1991d4cfe85d5043241acac92962c3977c8d2f2add1ee73130c7b286417d1d34" + url: "https://pub.dev" + source: hosted + version: "0.6.17" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" + url: "https://pub.dev" + source: hosted + version: "15.2.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" +sdks: + dart: ">=3.7.0 <4.0.0" diff --git a/packages/data_fixture_dart/pubspec.yaml b/packages/data_fixture_dart/pubspec.yaml new file mode 100644 index 0000000..1f0954f --- /dev/null +++ b/packages/data_fixture_dart/pubspec.yaml @@ -0,0 +1,13 @@ +name: data_fixture_dart +description: Creation of data model easily, with no headache. +version: 4.0.0 +repository: https://github.com/MyLittleSuite/data_fixture_dart + +environment: + sdk: ">=3.0.0 <4.0.0" + +dependencies: + faker: ^2.0.0 + +dev_dependencies: + test: ^1.24.0 diff --git a/test/fixture_factory_test.dart b/packages/data_fixture_dart/test/fixture_factory_test.dart similarity index 100% rename from test/fixture_factory_test.dart rename to packages/data_fixture_dart/test/fixture_factory_test.dart diff --git a/test/models/company.dart b/packages/data_fixture_dart/test/models/company.dart similarity index 100% rename from test/models/company.dart rename to packages/data_fixture_dart/test/models/company.dart diff --git a/test/models/dog.dart b/packages/data_fixture_dart/test/models/dog.dart similarity index 100% rename from test/models/dog.dart rename to packages/data_fixture_dart/test/models/dog.dart diff --git a/test/models/person.dart b/packages/data_fixture_dart/test/models/person.dart similarity index 100% rename from test/models/person.dart rename to packages/data_fixture_dart/test/models/person.dart diff --git a/packages/data_fixture_dart_annotations/lib/data_fixture_dart_annotations.dart b/packages/data_fixture_dart_annotations/lib/data_fixture_dart_annotations.dart new file mode 100644 index 0000000..dcc4344 --- /dev/null +++ b/packages/data_fixture_dart_annotations/lib/data_fixture_dart_annotations.dart @@ -0,0 +1,3 @@ +export 'src/faker_type.dart'; +export 'src/fixture_for.dart'; +export 'src/fixture_trait_value.dart'; diff --git a/packages/data_fixture_dart_annotations/lib/src/faker_type.dart b/packages/data_fixture_dart_annotations/lib/src/faker_type.dart new file mode 100644 index 0000000..ce54ede --- /dev/null +++ b/packages/data_fixture_dart_annotations/lib/src/faker_type.dart @@ -0,0 +1,16 @@ +enum FakerType { + personFirstName, + personLastName, + personName, + internetEmail, + internetUrl, + dateDateTime, + loremWord, + loremSentence, + randomInt, + randomDouble, + addressCity, + addressCountry, + phoneNumber, + companyName, +} diff --git a/packages/data_fixture_dart_annotations/lib/src/fixture_for.dart b/packages/data_fixture_dart_annotations/lib/src/fixture_for.dart new file mode 100644 index 0000000..4010d9e --- /dev/null +++ b/packages/data_fixture_dart_annotations/lib/src/fixture_for.dart @@ -0,0 +1,20 @@ +class FixtureFor { + final Type modelType; + final String? constructor; + final bool? hasJson; + + /// Keys are Symbols matching constructor parameter names. + /// Values are FakerType enum values. + final Map fields; + + /// Trait name → map of param Symbol → FakerType or FixtureTraitValue. + final Map> traits; + + const FixtureFor( + this.modelType, { + this.constructor, + this.hasJson, + this.fields = const {}, + this.traits = const {}, + }); +} diff --git a/packages/data_fixture_dart_annotations/lib/src/fixture_trait_value.dart b/packages/data_fixture_dart_annotations/lib/src/fixture_trait_value.dart new file mode 100644 index 0000000..2c524d7 --- /dev/null +++ b/packages/data_fixture_dart_annotations/lib/src/fixture_trait_value.dart @@ -0,0 +1,13 @@ +import 'faker_type.dart'; + +class FixtureTraitValue { + final Object? literalValue; + final FakerType? fakerType; + + const FixtureTraitValue.literal(this.literalValue) : fakerType = null; + const FixtureTraitValue.faker(FakerType type) + : fakerType = type, + literalValue = null; + + bool get isLiteral => fakerType == null; +} diff --git a/packages/data_fixture_dart_annotations/pubspec.lock b/packages/data_fixture_dart_annotations/pubspec.lock new file mode 100644 index 0000000..573410f --- /dev/null +++ b/packages/data_fixture_dart_annotations/pubspec.lock @@ -0,0 +1,5 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: {} +sdks: + dart: ">=3.0.0 <4.0.0" diff --git a/packages/data_fixture_dart_annotations/pubspec.yaml b/packages/data_fixture_dart_annotations/pubspec.yaml new file mode 100644 index 0000000..6b04880 --- /dev/null +++ b/packages/data_fixture_dart_annotations/pubspec.yaml @@ -0,0 +1,7 @@ +name: data_fixture_dart_annotations +description: Annotations for data_fixture_dart code generation. +version: 1.0.0 +repository: https://github.com/MyLittleSuite/data_fixture_dart + +environment: + sdk: ">=3.0.0 <4.0.0" diff --git a/packages/data_fixture_dart_generator/build.yaml b/packages/data_fixture_dart_generator/build.yaml new file mode 100644 index 0000000..6959f29 --- /dev/null +++ b/packages/data_fixture_dart_generator/build.yaml @@ -0,0 +1,10 @@ +builders: + data_fixture_dart_generator: + import: "package:data_fixture_dart_generator/builder.dart" + builder_factories: ["fixtureGeneratorBuilder"] + build_extensions: {".dart": [".fixture.dart"]} + auto_apply: dependents + build_to: source + required_inputs: + - ".freezed.dart" + - ".g.dart" diff --git a/packages/data_fixture_dart_generator/lib/builder.dart b/packages/data_fixture_dart_generator/lib/builder.dart new file mode 100644 index 0000000..e5db527 --- /dev/null +++ b/packages/data_fixture_dart_generator/lib/builder.dart @@ -0,0 +1,7 @@ +import 'package:build/build.dart'; +import 'package:source_gen/source_gen.dart'; + +import 'src/fixture_generator.dart'; + +Builder fixtureGeneratorBuilder(BuilderOptions options) => + LibraryBuilder(FixtureGenerator(), generatedExtension: '.fixture.dart'); diff --git a/packages/data_fixture_dart_generator/lib/src/constructor_resolver.dart b/packages/data_fixture_dart_generator/lib/src/constructor_resolver.dart new file mode 100644 index 0000000..90c95fc --- /dev/null +++ b/packages/data_fixture_dart_generator/lib/src/constructor_resolver.dart @@ -0,0 +1,103 @@ +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/nullability_suffix.dart'; +import 'package:analyzer/dart/element/type.dart'; + +class ResolvedParam { + final String name; + final String dartTypeName; + final bool isNullable; + final bool isNamed; + final bool isList; + final String? customTypeName; // non-null when type is an unknown class + final ClassElement? customClassElement; // non-null when customTypeName != null + final ClassElement? listElementClassElement; // non-null when isList && element type is custom class + + const ResolvedParam({ + required this.name, + required this.dartTypeName, + required this.isNullable, + required this.isNamed, + required this.isList, + this.customTypeName, + this.customClassElement, + this.listElementClassElement, + }); +} + +/// Finds the target constructor and extracts its parameters as [ResolvedParam]s. +class ConstructorResolver { + /// [constructorName] is null → unnamed constructor, '' → also unnamed. + List resolve(ClassElement classElement, String? constructorName) { + final constructor = _findConstructor(classElement, constructorName); + return constructor.parameters.map(_resolveParam).toList(); + } + + /// Returns the call prefix: e.g. `Dog` or `Dog.fromData`. + String callPrefix(ClassElement classElement, String? constructorName) { + final name = constructorName?.isNotEmpty == true ? constructorName! : null; + return name != null ? '${classElement.name}.$name' : classElement.name; + } + + ConstructorElement _findConstructor(ClassElement classElement, String? name) { + final constructors = classElement.constructors; + if (name != null && name.isNotEmpty) { + for (final c in constructors) { + if (c.name == name) return c; + } + throw StateError('Constructor "$name" not found on ${classElement.name}'); + } + // Prefer unnamed constructor; fall back to first. + for (final c in constructors) { + if (c.name.isEmpty) return c; + } + return constructors.first; + } + + ResolvedParam _resolveParam(ParameterElement p) { + final type = p.type; + final isNullable = type.nullabilitySuffix == NullabilitySuffix.question; + final isList = type.isDartCoreList; + + String typeName; + String? customType; + ClassElement? customClassEl; + ClassElement? listElementClassEl; + + const knownTypes = {'int', 'double', 'String', 'bool', 'DateTime', 'List', 'Map'}; + + if (type is InterfaceType) { + typeName = type.element.name; + if (isList) { + // Extract element type for List for nested auto-gen tracking + if (type.typeArguments.isNotEmpty) { + final elementType = type.typeArguments.first; + if (elementType is InterfaceType && + elementType.element is ClassElement && + elementType.element is! EnumElement && + !knownTypes.contains(elementType.element.name)) { + listElementClassEl = elementType.element as ClassElement; + } + } + } else if (type.element is! EnumElement && !knownTypes.contains(typeName)) { + // Enums cannot have a FixtureFactory — leave customType null so the + // field resolver emits a TODO placeholder instead of makeSingle(). + customType = typeName; + customClassEl = type.element is ClassElement ? type.element as ClassElement : null; + } + } else { + // ignore: deprecated_member_use + typeName = type.getDisplayString(withNullability: false); + } + + return ResolvedParam( + name: p.name, + dartTypeName: typeName, + isNullable: isNullable, + isNamed: p.isNamed, + isList: isList, + customTypeName: customType, + customClassElement: customClassEl, + listElementClassElement: listElementClassEl, + ); + } +} diff --git a/packages/data_fixture_dart_generator/lib/src/faker_type_mapping.dart b/packages/data_fixture_dart_generator/lib/src/faker_type_mapping.dart new file mode 100644 index 0000000..2c23234 --- /dev/null +++ b/packages/data_fixture_dart_generator/lib/src/faker_type_mapping.dart @@ -0,0 +1,18 @@ +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; + +const Map fakerTypeToCode = { + FakerType.personFirstName: 'faker.person.firstName()', + FakerType.personLastName: 'faker.person.lastName()', + FakerType.personName: 'faker.person.name()', + FakerType.internetEmail: 'faker.internet.email()', + FakerType.internetUrl: 'faker.internet.url()', + FakerType.dateDateTime: 'faker.date.dateTime()', + FakerType.loremWord: 'faker.lorem.word()', + FakerType.loremSentence: 'faker.lorem.sentence()', + FakerType.randomInt: 'faker.randomGenerator.integer(100)', + FakerType.randomDouble: 'faker.randomGenerator.decimal()', + FakerType.addressCity: 'faker.address.city()', + FakerType.addressCountry: 'faker.address.country()', + FakerType.phoneNumber: 'faker.phoneNumber.us()', + FakerType.companyName: 'faker.company.name()', +}; diff --git a/packages/data_fixture_dart_generator/lib/src/field_resolver.dart b/packages/data_fixture_dart_generator/lib/src/field_resolver.dart new file mode 100644 index 0000000..a702377 --- /dev/null +++ b/packages/data_fixture_dart_generator/lib/src/field_resolver.dart @@ -0,0 +1,47 @@ +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; + +import 'faker_type_mapping.dart'; +import 'similarity.dart'; + +/// Resolves the faker code expression for a constructor parameter. +/// Pure function — no analyzer dependency. Takes pre-extracted type info. +String resolveFieldExpr({ + required String paramName, + required String dartTypeName, + required bool isNullable, + required bool isList, + required String? customTypeName, // non-null when type is a custom class + required FakerType? explicitFakerType, // layer 3 override +}) { + // Layer 3: explicit @FixtureFor fields map override + if (explicitFakerType != null) { + return fakerTypeToCode[explicitFakerType]!; + } + + // Nullable: return null before any other resolution + if (isNullable) return 'null'; + + // id convention + if (paramName.toLowerCase() == 'id' && dartTypeName == 'int') return 'index'; + + // Layer 2: similarity-based name matching + final similarityMatch = bestDictionaryMatch(paramName); + if (similarityMatch != null) return similarityMatch; + + // Layer 1: type-based default + return _typeDefault(dartTypeName, isList, customTypeName); +} + +String _typeDefault(String typeName, bool isList, String? customTypeName) { + if (isList) return '[]'; + return switch (typeName) { + 'int' => 'faker.randomGenerator.integer(100)', + 'double' => 'faker.randomGenerator.decimal()', + 'String' => 'faker.lorem.word()', + 'bool' => 'faker.randomGenerator.boolean()', + 'DateTime' => 'faker.date.dateTime()', + _ => customTypeName != null + ? '${customTypeName}Fixture.factory().makeSingle()' + : 'null /* TODO: provide value for $typeName */', + }; +} diff --git a/packages/data_fixture_dart_generator/lib/src/fixture_generator.dart b/packages/data_fixture_dart_generator/lib/src/fixture_generator.dart new file mode 100644 index 0000000..6aded9d --- /dev/null +++ b/packages/data_fixture_dart_generator/lib/src/fixture_generator.dart @@ -0,0 +1,320 @@ +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:build/build.dart'; +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:source_gen/source_gen.dart'; + +import 'constructor_resolver.dart'; +import 'field_resolver.dart'; +import 'json_builder_resolver.dart'; +import 'trait_generator.dart'; + +const _fixtureForChecker = TypeChecker.fromRuntime(FixtureFor); + +typedef _PendingEntry = ({ClassElement element, bool inheritJson}); + +class FixtureGenerator extends Generator { + final _constructorResolver = ConstructorResolver(); + final _jsonResolver = JsonBuilderResolver(); + final _traitGen = TraitGenerator(); + + @override + String generate(LibraryReader library, BuildStep buildStep) { + final imports = { + "import 'package:data_fixture_dart/data_fixture_dart.dart';", + }; + + final bodies = StringBuffer(); + final generatedNames = {}; + final pendingAutoGen = {}; + + // Phase 1: explicit @FixtureFor annotations + for (final element in library.allElements) { + for (final annotation in _fixtureForChecker.annotationsOf(element)) { + final reader = ConstantReader(annotation); + final modelTypeValue = reader.read('modelType').typeValue; + final classEl = (modelTypeValue as InterfaceType).element as ClassElement; + generatedNames.add(classEl.name); + + final output = _generateForAnnotation(reader, imports, pendingAutoGen, generatedNames); + bodies.writeln(output); + } + } + + // Phase 2: auto-generate base factories for nested types + while (pendingAutoGen.isNotEmpty) { + final entry = pendingAutoGen.entries.first; + pendingAutoGen.remove(entry.key); + if (generatedNames.contains(entry.key)) continue; + generatedNames.add(entry.key); + + final output = _generateBaseFactory( + entry.value.element, + entry.value.inheritJson, + imports, + pendingAutoGen, + generatedNames, + ); + bodies.writeln(output); + } + + if (bodies.isEmpty) return ''; + + final buffer = StringBuffer(); + buffer.writeln('// ignore_for_file: type=lint'); + buffer.writeln(); + for (final imp in imports) { + buffer.writeln(imp); + } + buffer.writeln(); + buffer.write(bodies); + + return buffer.toString(); + } + + String _generateForAnnotation( + ConstantReader annotation, + Set imports, + Map pendingAutoGen, + Set generatedNames, + ) { + // Read model type + final modelTypeValue = annotation.read('modelType').typeValue; + final element = (modelTypeValue as InterfaceType).element; + if (element is! ClassElement || element.isAbstract) { + throw InvalidGenerationSourceError( + '@FixtureFor requires a concrete, non-abstract class. ' + '"${element.name}" is not a valid target.', + element: element, + ); + } + final classElement = element; + final className = classElement.name; + + // Add model import — normalize asset: URIs to package: form + final uri = classElement.library.source.uri; + final importUri = _normalizeUri(uri); + imports.add("import '$importUri';"); + + // Read annotation params + final constructorName = annotation.peek('constructor')?.stringValue; + final hasJsonOverride = annotation.peek('hasJson')?.boolValue; + + // Resolve constructor params + final params = _constructorResolver.resolve(classElement, constructorName); + final callPrefix = _constructorResolver.callPrefix(classElement, constructorName); + + // Build explicit fields map: paramName → FakerType + final explicitFields = {}; + final fieldsMap = annotation.read('fields').mapValue; + fieldsMap.forEach((keyObj, valueObj) { + final paramName = keyObj!.toSymbolValue()!; + final enumIndex = valueObj!.getField('index')?.toIntValue(); + if (enumIndex == null || enumIndex >= FakerType.values.length) { + throw InvalidGenerationSourceError( + '@FixtureFor fields map value for #$paramName must be a FakerType enum value.', + element: classElement, + ); + } + explicitFields[paramName] = FakerType.values[enumIndex]; + }); + + // Resolve faker expression per param + final paramExprList = params.map((p) { + final expr = resolveFieldExpr( + paramName: p.name, + dartTypeName: p.dartTypeName, + isNullable: p.isNullable, + isList: p.isList, + customTypeName: p.customTypeName, + explicitFakerType: explicitFields[p.name], + ); + return p.isNamed ? '${p.name}: $expr' : expr; + }).toList(); + final paramExprs = paramExprList.isEmpty + ? '' + : '\n ${paramExprList.join(',\n ')},\n '; + + // Determine JSON strategy + final useToJson = hasJsonOverride ?? _jsonResolver.shouldUseToJson(classElement); + final isJsonFactory = useToJson; + + // Enqueue nested types for auto-generation + _enqueueNestedTypes(params, isJsonFactory, pendingAutoGen, generatedNames); + + // Resolve traits + final traitsMap = annotation.read('traits').mapValue; + final traitMethods = StringBuffer(); + traitsMap.forEach((traitKeyObj, traitValueObj) { + final traitName = traitKeyObj!.toStringValue()!; + if (!RegExp(r'^[a-zA-Z_$][a-zA-Z0-9_$]*$').hasMatch(traitName)) { + throw InvalidGenerationSourceError( + 'Trait name "$traitName" is not a valid Dart identifier.', + element: classElement, + ); + } + final overrides = {}; + + traitValueObj!.toMapValue()!.forEach((paramKeyObj, paramValueObj) { + final paramName = paramKeyObj!.toSymbolValue()!; + final typeDisplayName = paramValueObj!.type + ?.getDisplayString(withNullability: false) // ignore: deprecated_member_use + ; + + if (typeDisplayName == 'FakerType') { + final enumIndex = paramValueObj.getField('index')!.toIntValue()!; + overrides[paramName] = + TraitGenerator.overrideToExpr(null, FakerType.values[enumIndex]); + } else { + // FixtureTraitValue + final fakerTypeField = paramValueObj.getField('fakerType'); + final fakerTypeIndex = fakerTypeField?.getField('index')?.toIntValue(); + if (fakerTypeIndex != null) { + overrides[paramName] = TraitGenerator.overrideToExpr( + null, + FakerType.values[fakerTypeIndex], + ); + } else { + final literalObj = paramValueObj.getField('literalValue'); + final literal = literalObj?.toIntValue() ?? + literalObj?.toDoubleValue() ?? + literalObj?.toBoolValue() ?? + literalObj?.toStringValue(); + overrides[paramName] = TraitGenerator.overrideToExpr(literal, null); + } + } + }); + + traitMethods.writeln(_traitGen.generate( + traitName: traitName, + className: className, + params: params, + callPrefix: callPrefix, + overrides: overrides, + isJsonFactory: isJsonFactory, + )); + }); + + final baseClass = isJsonFactory + ? 'JsonFixtureFactory<$className>' + : 'FixtureFactory<$className>'; + + final definitionBody = ''' + @override + FixtureDefinition<$className> definition() => define( + (faker, [int index = 0]) => $callPrefix($paramExprs), + );'''; + + final jsonBody = isJsonFactory + ? _jsonResolver.generate( + className: className, + useToJson: useToJson, + paramNames: params.map((p) => p.name).toList(), + ) + : ''; + + final traitSection = traitMethods.isNotEmpty ? '\n$traitMethods' : ''; + + return ''' +class _\$${className}FixtureFactory extends $baseClass { +$definitionBody +${jsonBody.isNotEmpty ? '\n$jsonBody\n' : ''}$traitSection} + +extension ${className}Fixture on $className { + static _\$${className}FixtureFactory factory() => _\$${className}FixtureFactory(); +} +'''; + } + + String _generateBaseFactory( + ClassElement classElement, + bool inheritJson, + Set imports, + Map pendingAutoGen, + Set generatedNames, + ) { + final className = classElement.name; + + final uri = classElement.library.source.uri; + imports.add("import '${_normalizeUri(uri)}';"); + + final params = _constructorResolver.resolve(classElement, null); + final callPrefix = _constructorResolver.callPrefix(classElement, null); + + // Enqueue further nested types, propagating inheritJson + _enqueueNestedTypes(params, inheritJson, pendingAutoGen, generatedNames); + + final paramExprList = params.map((p) { + final expr = resolveFieldExpr( + paramName: p.name, + dartTypeName: p.dartTypeName, + isNullable: p.isNullable, + isList: p.isList, + customTypeName: p.customTypeName, + explicitFakerType: null, + ); + return p.isNamed ? '${p.name}: $expr' : expr; + }).toList(); + final paramExprs = paramExprList.isEmpty + ? '' + : '\n ${paramExprList.join(',\n ')},\n '; + + // Use JsonFixtureFactory only if parent requested json AND model has toJson() + final useJson = inheritJson && _jsonResolver.shouldUseToJson(classElement); + final baseClass = + useJson ? 'JsonFixtureFactory<$className>' : 'FixtureFactory<$className>'; + + final definitionBody = ''' + @override + FixtureDefinition<$className> definition() => define( + (faker, [int index = 0]) => $callPrefix($paramExprs), + );'''; + + final jsonBody = useJson + ? _jsonResolver.generate( + className: className, + useToJson: true, + paramNames: params.map((p) => p.name).toList(), + ) + : ''; + + return ''' +class _\$${className}FixtureFactory extends $baseClass { +$definitionBody +${jsonBody.isNotEmpty ? '\n$jsonBody\n' : ''}} + +extension ${className}Fixture on $className { + static _\$${className}FixtureFactory factory() => _\$${className}FixtureFactory(); +} +'''; + } + + void _enqueueNestedTypes( + List params, + bool inheritJson, + Map pendingAutoGen, + Set generatedNames, + ) { + for (final p in params) { + final classEl = p.customClassElement ?? p.listElementClassElement; + if (classEl == null) continue; + final name = classEl.name; + if (!generatedNames.contains(name) && !pendingAutoGen.containsKey(name)) { + pendingAutoGen[name] = (element: classEl, inheritJson: inheritJson); + } + } + } + + String _normalizeUri(Uri uri) { + if (uri.scheme == 'asset') { + // asset:pkg/lib/path/file.dart → package:pkg/path/file.dart + final segments = uri.pathSegments; + if (segments.length >= 3 && segments[1] == 'lib') { + final packageName = segments[0]; + final rest = segments.sublist(2).join('/'); + return 'package:$packageName/$rest'; + } + } + return uri.toString(); + } +} diff --git a/packages/data_fixture_dart_generator/lib/src/json_builder_resolver.dart b/packages/data_fixture_dart_generator/lib/src/json_builder_resolver.dart new file mode 100644 index 0000000..25c6a05 --- /dev/null +++ b/packages/data_fixture_dart_generator/lib/src/json_builder_resolver.dart @@ -0,0 +1,52 @@ +import 'package:analyzer/dart/element/element.dart'; +import 'package:source_gen/source_gen.dart'; + +const _jsonSerializableChecker = TypeChecker.fromUrl( + 'package:json_annotation/json_annotation.dart#JsonSerializable', +); +const _freezedChecker = TypeChecker.fromUrl( + 'package:freezed_annotation/freezed_annotation.dart#Freezed', +); + +class JsonBuilderResolver { + /// Returns true if the model has a `toJson()` method or is annotated with + /// @JsonSerializable / @freezed. + bool shouldUseToJson(ClassElement classElement) { + if (classElement.methods.any((m) => m.name == 'toJson')) return true; + if (_jsonSerializableChecker.hasAnnotationOf(classElement)) return true; + if (_freezedChecker.hasAnnotationOf(classElement)) return true; + return false; + } + + /// Generates the `jsonDefinition()` override body. + String generate({ + required String className, + required bool useToJson, + required List paramNames, + }) { + final varName = _varName(className); + + if (useToJson) { + return ''' + @override + JsonFixtureDefinition<$className> jsonDefinition() => defineJson( + ($varName, [int index = 0]) => $varName.toJson(), + );'''; + } + + final fieldLines = paramNames + .map((n) => " '$n': $varName.$n") + .join(',\n'); + final fieldsBlock = fieldLines.isEmpty ? '' : '$fieldLines,\n'; + + return ''' + @override + JsonFixtureDefinition<$className> jsonDefinition() => defineJson( + ($varName, [int index = 0]) => { +${fieldsBlock} }, + );'''; + } + + String _varName(String className) => + className[0].toLowerCase() + className.substring(1); +} diff --git a/packages/data_fixture_dart_generator/lib/src/similarity.dart b/packages/data_fixture_dart_generator/lib/src/similarity.dart new file mode 100644 index 0000000..e6d9fcc --- /dev/null +++ b/packages/data_fixture_dart_generator/lib/src/similarity.dart @@ -0,0 +1,93 @@ +import 'dart:math'; + +/// Normalizes a field name: strips underscores and lowercases. +/// camelCase fields like `firstName` become `firstname`. +String normalize(String name) { + return name.replaceAll('_', '').toLowerCase(); +} + +/// Jaro-Winkler similarity between two strings. Returns 0.0..1.0. +double jaroWinkler(String s1, String s2) { + if (s1 == s2) return 1.0; + if (s1.isEmpty || s2.isEmpty) return 0.0; + + final matchDistance = max(s1.length, s2.length) ~/ 2 - 1; + if (matchDistance < 0) return 0.0; + + final s1Matches = List.filled(s1.length, false); + final s2Matches = List.filled(s2.length, false); + + var matches = 0; + for (var i = 0; i < s1.length; i++) { + final start = max(0, i - matchDistance); + final end = min(s2.length, i + matchDistance + 1); + for (var j = start; j < end; j++) { + if (s2Matches[j] || s1[i] != s2[j]) continue; + s1Matches[i] = true; + s2Matches[j] = true; + matches++; + break; + } + } + + if (matches == 0) return 0.0; + + var transpositions = 0; + var k = 0; + for (var i = 0; i < s1.length; i++) { + if (!s1Matches[i]) continue; + while (!s2Matches[k]) k++; + if (s1[i] != s2[k]) transpositions++; + k++; + } + + final jaro = (matches / s1.length + + matches / s2.length + + (matches - transpositions / 2) / matches) / + 3; + + var prefix = 0; + for (var i = 0; i < min(4, min(s1.length, s2.length)); i++) { + if (s1[i] != s2[i]) break; + prefix++; + } + + return jaro + prefix * 0.1 * (1 - jaro); +} + +const double _threshold = 0.85; + +/// Dictionary of normalized name patterns → faker code expressions. +const List<(String, String)> _dictionary = [ + ('firstname', 'faker.person.firstName()'), + ('lastname', 'faker.person.lastName()'), + ('name', 'faker.person.name()'), + ('email', 'faker.internet.email()'), + ('url', 'faker.internet.url()'), + ('birthday', 'faker.date.dateTime()'), + ('date', 'faker.date.dateTime()'), + ('phone', 'faker.phoneNumber.us()'), + ('city', 'faker.address.city()'), + ('country', 'faker.address.country()'), + ('company', 'faker.company.name()'), + ('description', 'faker.lorem.sentence()'), + ('title', 'faker.lorem.word()'), +]; + +/// Returns the best matching faker code expression for [fieldName], or null if +/// no dictionary entry scores above the threshold. +String? bestDictionaryMatch(String fieldName) { + final normalized = normalize(fieldName); + String? best; + var bestScore = _threshold; + + for (final (pattern, code) in _dictionary) { + final score = jaroWinkler(normalized, pattern); + if (score >= bestScore) { + bestScore = score; + best = code; + } + } + + return best; +} diff --git a/packages/data_fixture_dart_generator/lib/src/trait_generator.dart b/packages/data_fixture_dart_generator/lib/src/trait_generator.dart new file mode 100644 index 0000000..4784487 --- /dev/null +++ b/packages/data_fixture_dart_generator/lib/src/trait_generator.dart @@ -0,0 +1,56 @@ +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; + +import 'constructor_resolver.dart'; +import 'faker_type_mapping.dart'; + +class TraitGenerator { + /// Generates a named trait method for a factory. + /// + /// [traitName] e.g. `'old'` + /// [overrides] map of paramName → code expression string + /// [params] all constructor params (for defaults: `varName.paramName`) + /// [callPrefix] e.g. `Dog` or `Dog.fromData` + /// [isJsonFactory] whether factory extends JsonFixtureFactory + String generate({ + required String traitName, + required String className, + required List params, + required String callPrefix, + required Map overrides, // paramName → code expression + required bool isJsonFactory, + }) { + final varName = className[0].toLowerCase() + className.substring(1); + final returnType = isJsonFactory + ? 'JsonFixtureDefinition<$className>' + : 'FixtureDefinition<$className>'; + final redefineMethod = isJsonFactory ? 'redefineJson' : 'redefine'; + + final paramExprList = params.map((p) { + final expr = overrides[p.name] ?? '$varName.${p.name}'; + return p.isNamed ? '${p.name}: $expr' : expr; + }).toList(); + final paramExprs = paramExprList.isEmpty + ? '' + : '\n ${paramExprList.join(',\n ')},\n '; + + return ''' + $returnType $traitName() => $redefineMethod( + ($varName, [int index = 0]) => $callPrefix($paramExprs), + );'''; + } + + /// Converts a trait override value to a Dart code string. + /// Pass either a [fakerType] or a [literalValue] (int, double, bool, String, or null). + static String overrideToExpr(Object? literalValue, FakerType? fakerType) { + if (fakerType != null) { + return fakerTypeToCode[fakerType] ?? + (throw StateError('No code mapping for FakerType.$fakerType')); + } + if (literalValue == null) return 'null'; + if (literalValue is String) { + final escaped = literalValue.replaceAll(r'\', r'\\').replaceAll("'", r"\'"); + return "'$escaped'"; + } + return '$literalValue'; // int, double, bool + } +} diff --git a/packages/data_fixture_dart_generator/pubspec.lock b/packages/data_fixture_dart_generator/pubspec.lock new file mode 100644 index 0000000..ef17161 --- /dev/null +++ b/packages/data_fixture_dart_generator/pubspec.lock @@ -0,0 +1,585 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" + url: "https://pub.dev" + source: hosted + version: "76.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.3.3" + analyzer: + dependency: "direct main" + description: + name: analyzer + sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" + url: "https://pub.dev" + source: hosted + version: "6.11.0" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.dev" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + build: + dependency: "direct main" + description: + name: build + sha256: "51dc711996cbf609b90cbe5b335bbce83143875a9d58e4b5c6d3c4f684d3dda7" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + build_config: + dependency: transitive + description: + name: build_config + sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 + url: "https://pub.dev" + source: hosted + version: "4.1.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: ee4257b3f20c0c90e72ed2b57ad637f694ccba48839a821e87db762548c22a62 + url: "https://pub.dev" + source: hosted + version: "2.5.4" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "382a4d649addbfb7ba71a3631df0ec6a45d5ab9b098638144faf27f02778eb53" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "85fbbb1036d576d966332a3f5ce83f2ce66a40bea1a94ad2d5fc29a19a0d3792" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + build_test: + dependency: "direct dev" + description: + name: build_test + sha256: e7d9f53a36c4a1e449af1891198c74c2397147f06328e95e51be93e4f9b41573 + url: "https://pub.dev" + source: hosted + version: "3.2.1" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "34e4067d30ce212937df995f03b69992eea683539ceeac7f679a1f1eba055b56" + url: "https://pub.dev" + source: hosted + version: "8.12.6" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" + url: "https://pub.dev" + source: hosted + version: "4.11.1" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" + url: "https://pub.dev" + source: hosted + version: "1.15.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820" + url: "https://pub.dev" + source: hosted + version: "2.3.8" + data_fixture_dart_annotations: + dependency: "direct main" + description: + path: "../data_fixture_dart_annotations" + relative: true + source: path + version: "1.0.0" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" + http: + dependency: transitive + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + js: + dependency: transitive + description: + name: js + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + macros: + dependency: transitive + description: + name: macros + sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" + url: "https://pub.dev" + source: hosted + version: "0.1.3-main.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + meta: + dependency: transitive + description: + name: meta + sha256: df0c643f44ad098eb37988027a8e2b2b5a031fd3977f06bbfd3a76637e8df739 + url: "https://pub.dev" + source: hosted + version: "1.18.2" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + pool: + dependency: transitive + description: + name: pool + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + url: "https://pub.dev" + source: hosted + version: "1.5.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + source_gen: + dependency: "direct main" + description: + name: source_gen + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + url: "https://pub.dev" + source: hosted + version: "0.10.13" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test: + dependency: "direct dev" + description: + name: test + sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" + url: "https://pub.dev" + source: hosted + version: "1.26.3" + test_api: + dependency: transitive + description: + name: test_api + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + url: "https://pub.dev" + source: hosted + version: "0.7.7" + test_core: + dependency: transitive + description: + name: test_core + sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" + url: "https://pub.dev" + source: hosted + version: "0.6.12" + timing: + dependency: transitive + description: + name: timing + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" + url: "https://pub.dev" + source: hosted + version: "15.2.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" +sdks: + dart: ">=3.7.0 <4.0.0" diff --git a/packages/data_fixture_dart_generator/pubspec.yaml b/packages/data_fixture_dart_generator/pubspec.yaml new file mode 100644 index 0000000..1661373 --- /dev/null +++ b/packages/data_fixture_dart_generator/pubspec.yaml @@ -0,0 +1,18 @@ +name: data_fixture_dart_generator +description: Code generator for data_fixture_dart. +version: 1.0.0 +repository: https://github.com/MyLittleSuite/data_fixture_dart + +environment: + sdk: ">=3.7.0 <4.0.0" + +dependencies: + analyzer: ^6.4.0 + build: ^2.4.0 + source_gen: ^1.5.0 + data_fixture_dart_annotations: ^1.0.0 + +dev_dependencies: + build_test: ^3.0.0 + test: ^1.24.0 + build_runner: ^2.4.0 diff --git a/packages/data_fixture_dart_generator/test/field_resolver_test.dart b/packages/data_fixture_dart_generator/test/field_resolver_test.dart new file mode 100644 index 0000000..6779c75 --- /dev/null +++ b/packages/data_fixture_dart_generator/test/field_resolver_test.dart @@ -0,0 +1,160 @@ +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:data_fixture_dart_generator/src/faker_type_mapping.dart'; +import 'package:data_fixture_dart_generator/src/field_resolver.dart'; +import 'package:test/test.dart'; + +void main() { + group('fakerTypeToCode', () { + test('covers all FakerType values', () { + for (final type in FakerType.values) { + expect( + fakerTypeToCode[type], + isNotNull, + reason: 'Missing mapping for FakerType.$type', + ); + } + }); + }); + + group('resolveFieldExpr', () { + test('layer 3: explicit fields map wins over everything', () { + expect( + resolveFieldExpr( + paramName: 'name', + dartTypeName: 'String', + isNullable: false, + isList: false, + customTypeName: null, + explicitFakerType: FakerType.loremSentence, + ), + 'faker.lorem.sentence()', + ); + }); + + test('layer 2: similarity match for email', () { + expect( + resolveFieldExpr( + paramName: 'email', + dartTypeName: 'String', + isNullable: false, + isList: false, + customTypeName: null, + explicitFakerType: null, + ), + 'faker.internet.email()', + ); + }); + + test('layer 2: similarity match for firstName', () { + expect( + resolveFieldExpr( + paramName: 'firstName', + dartTypeName: 'String', + isNullable: false, + isList: false, + customTypeName: null, + explicitFakerType: null, + ), + 'faker.person.firstName()', + ); + }); + + test('layer 1: String fallback for unmatched name', () { + expect( + resolveFieldExpr( + paramName: 'xyzUnknown', + dartTypeName: 'String', + isNullable: false, + isList: false, + customTypeName: null, + explicitFakerType: null, + ), + 'faker.lorem.word()', + ); + }); + + test('layer 1: int fallback', () { + expect( + resolveFieldExpr( + paramName: 'numItems', + dartTypeName: 'int', + isNullable: false, + isList: false, + customTypeName: null, + explicitFakerType: null, + ), + 'faker.randomGenerator.integer(100)', + ); + }); + + test('layer 1: nullable → null', () { + expect( + resolveFieldExpr( + paramName: 'birthday', + dartTypeName: 'DateTime', + isNullable: true, + isList: false, + customTypeName: null, + explicitFakerType: null, + ), + 'null', + ); + }); + + test('layer 3: explicit FakerType wins over nullable', () { + expect( + resolveFieldExpr( + paramName: 'nickname', + dartTypeName: 'String', + isNullable: true, + isList: false, + customTypeName: null, + explicitFakerType: FakerType.personFirstName, + ), + 'faker.person.firstName()', + ); + }); + + test('id int → index convention', () { + expect( + resolveFieldExpr( + paramName: 'id', + dartTypeName: 'int', + isNullable: false, + isList: false, + customTypeName: null, + explicitFakerType: null, + ), + 'index', + ); + }); + + test('List → []', () { + expect( + resolveFieldExpr( + paramName: 'items', + dartTypeName: 'List', + isNullable: false, + isList: true, + customTypeName: null, + explicitFakerType: null, + ), + '[]', + ); + }); + + test('custom type → factory call', () { + expect( + resolveFieldExpr( + paramName: 'owner', + dartTypeName: 'Owner', + isNullable: false, + isList: false, + customTypeName: 'Owner', + explicitFakerType: null, + ), + 'OwnerFixture.factory().makeSingle()', + ); + }); + }); +} diff --git a/packages/data_fixture_dart_generator/test/fixture_generator_test.dart b/packages/data_fixture_dart_generator/test/fixture_generator_test.dart new file mode 100644 index 0000000..2e6f85d --- /dev/null +++ b/packages/data_fixture_dart_generator/test/fixture_generator_test.dart @@ -0,0 +1,452 @@ +import 'package:build/build.dart'; +import 'package:build_test/build_test.dart'; +import 'package:data_fixture_dart_generator/builder.dart'; +import 'package:test/test.dart'; + +/// Creates a [TestReaderWriter] pre-loaded with all real isolate sources. +/// Each test gets a fresh one to avoid asset cross-contamination. +Future _makeReader() async { + final rw = TestReaderWriter(rootPackage: 'a'); + await rw.testing.loadIsolateSources(); + return rw; +} + +void main() { + + group('FixtureGenerator - nested auto-generation', () { + test('auto-generates base factory for nested custom type', () async { + await testBuilder( + fixtureGeneratorBuilder(BuilderOptions.empty), + { + 'a|lib/engine.dart': ''' +class Engine { + final int horsepower; + Engine({required this.horsepower}); +} +''', + 'a|lib/car.dart': ''' +import 'package:a/engine.dart'; +class Car { + final int id; + final Engine engine; + Car({required this.id, required this.engine}); +} +''', + 'a|test/fixtures.dart': ''' +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:a/car.dart'; + +@FixtureFor(Car) +void fixtures() {} +''', + }, + readerWriter: await _makeReader(), + outputs: { + 'a|test/fixtures.fixture.dart': decodedMatches(allOf( + contains('_\$CarFixtureFactory extends FixtureFactory'), + contains('engine: EngineFixture.factory().makeSingle()'), + contains('_\$EngineFixtureFactory extends FixtureFactory'), + contains('extension EngineFixture on Engine'), + )), + }, + ); + }); + + test('auto-generates base factory for List element', () async { + await testBuilder( + fixtureGeneratorBuilder(BuilderOptions.empty), + { + 'a|lib/wheel.dart': ''' +class Wheel { + final String brand; + Wheel({required this.brand}); +} +''', + 'a|lib/car.dart': ''' +import 'package:a/wheel.dart'; +class Car { + final int id; + final List wheels; + Car({required this.id, required this.wheels}); +} +''', + 'a|test/fixtures.dart': ''' +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:a/car.dart'; + +@FixtureFor(Car) +void fixtures() {} +''', + }, + readerWriter: await _makeReader(), + outputs: { + 'a|test/fixtures.fixture.dart': decodedMatches(allOf( + contains('_\$CarFixtureFactory extends FixtureFactory'), + contains('_\$WheelFixtureFactory extends FixtureFactory'), + contains('extension WheelFixture on Wheel'), + )), + }, + ); + }); + + test('hasJson propagates to nested type with toJson()', () async { + await testBuilder( + fixtureGeneratorBuilder(BuilderOptions.empty), + { + 'a|lib/engine.dart': ''' +class Engine { + final int horsepower; + Engine({required this.horsepower}); + Map toJson() => {'horsepower': horsepower}; +} +''', + 'a|lib/car.dart': ''' +import 'package:a/engine.dart'; +class Car { + final int id; + final Engine engine; + Car({required this.id, required this.engine}); + Map toJson() => {'id': id, 'engine': engine.toJson()}; +} +''', + 'a|test/fixtures.dart': ''' +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:a/car.dart'; + +@FixtureFor(Car, hasJson: true) +void fixtures() {} +''', + }, + readerWriter: await _makeReader(), + outputs: { + 'a|test/fixtures.fixture.dart': decodedMatches(allOf( + contains('_\$CarFixtureFactory extends JsonFixtureFactory'), + contains('_\$EngineFixtureFactory extends JsonFixtureFactory'), + contains('engine.toJson()'), + )), + }, + ); + }); + + test('hasJson does not make nested JsonFixtureFactory when nested has no toJson()', () async { + await testBuilder( + fixtureGeneratorBuilder(BuilderOptions.empty), + { + 'a|lib/engine.dart': ''' +class Engine { + final int horsepower; + Engine({required this.horsepower}); +} +''', + 'a|lib/car.dart': ''' +import 'package:a/engine.dart'; +class Car { + final int id; + final Engine engine; + Car({required this.id, required this.engine}); + Map toJson() => {'id': id}; +} +''', + 'a|test/fixtures.dart': ''' +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:a/car.dart'; + +@FixtureFor(Car, hasJson: true) +void fixtures() {} +''', + }, + readerWriter: await _makeReader(), + outputs: { + 'a|test/fixtures.fixture.dart': decodedMatches(allOf( + contains('_\$CarFixtureFactory extends JsonFixtureFactory'), + contains('_\$EngineFixtureFactory extends FixtureFactory'), + )), + }, + ); + }); + + test('explicit @FixtureFor for nested type wins over auto-gen', () async { + await testBuilder( + fixtureGeneratorBuilder(BuilderOptions.empty), + { + 'a|lib/engine.dart': ''' +class Engine { + final int horsepower; + Engine({required this.horsepower}); +} +''', + 'a|lib/car.dart': ''' +import 'package:a/engine.dart'; +class Car { + final int id; + final Engine engine; + Car({required this.id, required this.engine}); +} +''', + 'a|test/fixtures.dart': ''' +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:a/car.dart'; +import 'package:a/engine.dart'; + +@FixtureFor(Car) +@FixtureFor(Engine) +void fixtures() {} +''', + }, + readerWriter: await _makeReader(), + outputs: { + 'a|test/fixtures.fixture.dart': decodedMatches( + // Engine factory appears exactly once (from explicit @FixtureFor, not duplicated) + isNot(contains('class _\$EngineFixtureFactory extends FixtureFactory {\n' + ' @override\n' + ' FixtureDefinition definition()' + '\n' + 'class _\$EngineFixtureFactory')), + ), + }, + ); + }); + + test('auto-gen recurses through multiple levels', () async { + await testBuilder( + fixtureGeneratorBuilder(BuilderOptions.empty), + { + 'a|lib/spark_plug.dart': ''' +class SparkPlug { + final String brand; + SparkPlug({required this.brand}); +} +''', + 'a|lib/engine.dart': ''' +import 'package:a/spark_plug.dart'; +class Engine { + final int horsepower; + final SparkPlug plug; + Engine({required this.horsepower, required this.plug}); +} +''', + 'a|lib/car.dart': ''' +import 'package:a/engine.dart'; +class Car { + final int id; + final Engine engine; + Car({required this.id, required this.engine}); +} +''', + 'a|test/fixtures.dart': ''' +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:a/car.dart'; + +@FixtureFor(Car) +void fixtures() {} +''', + }, + readerWriter: await _makeReader(), + outputs: { + 'a|test/fixtures.fixture.dart': decodedMatches(allOf( + contains('_\$CarFixtureFactory'), + contains('_\$EngineFixtureFactory'), + contains('_\$SparkPlugFixtureFactory'), + contains('extension SparkPlugFixture on SparkPlug'), + )), + }, + ); + }); + }); + + group('FixtureGenerator', () { + test('generates FixtureFactory for plain model', () async { + await testBuilder( + fixtureGeneratorBuilder(BuilderOptions.empty), + { + 'a|lib/dog.dart': ''' +class Dog { + final int id; + final String name; + final int age; + Dog({required this.id, required this.name, required this.age}); +} +''', + 'a|test/fixtures.dart': ''' +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:a/dog.dart'; + +@FixtureFor(Dog) +void fixtures() {} +''', + }, + readerWriter: await _makeReader(), + outputs: { + 'a|test/fixtures.fixture.dart': decodedMatches(allOf( + contains('_\$DogFixtureFactory extends FixtureFactory'), + contains('extension DogFixture on Dog'), + )), + }, + ); + }); + + test('id int field uses index convention', () async { + await testBuilder( + fixtureGeneratorBuilder(BuilderOptions.empty), + { + 'a|lib/dog.dart': ''' +class Dog { + final int id; + final String name; + Dog({required this.id, required this.name}); +} +''', + 'a|test/fixtures.dart': ''' +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:a/dog.dart'; + +@FixtureFor(Dog) +void fixtures() {} +''', + }, + readerWriter: await _makeReader(), + outputs: { + 'a|test/fixtures.fixture.dart': decodedMatches(contains('id: index')), + }, + ); + }); + + test('explicit fields override wins', () async { + await testBuilder( + fixtureGeneratorBuilder(BuilderOptions.empty), + { + 'a|lib/dog.dart': ''' +class Dog { + final String name; + Dog({required this.name}); +} +''', + 'a|test/fixtures.dart': ''' +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:a/dog.dart'; + +@FixtureFor(Dog, fields: {#name: FakerType.loremSentence}) +void fixtures() {} +''', + }, + readerWriter: await _makeReader(), + outputs: { + 'a|test/fixtures.fixture.dart': decodedMatches(contains('faker.lorem.sentence()')), + }, + ); + }); + + test('named constructor', () async { + await testBuilder( + fixtureGeneratorBuilder(BuilderOptions.empty), + { + 'a|lib/dog.dart': ''' +class Dog { + final String name; + Dog.fromData({required this.name}); +} +''', + 'a|test/fixtures.dart': ''' +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:a/dog.dart'; + +@FixtureFor(Dog, constructor: 'fromData') +void fixtures() {} +''', + }, + readerWriter: await _makeReader(), + outputs: { + 'a|test/fixtures.fixture.dart': decodedMatches(contains('Dog.fromData(')), + }, + ); + }); + + test('hasJson: true generates JsonFixtureFactory with toJson', () async { + await testBuilder( + fixtureGeneratorBuilder(BuilderOptions.empty), + { + 'a|lib/dog.dart': ''' +class Dog { + final String name; + Dog({required this.name}); + Map toJson() => {'name': name}; +} +''', + 'a|test/fixtures.dart': ''' +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:a/dog.dart'; + +@FixtureFor(Dog, hasJson: true) +void fixtures() {} +''', + }, + readerWriter: await _makeReader(), + outputs: { + 'a|test/fixtures.fixture.dart': decodedMatches(allOf( + contains('JsonFixtureFactory'), + contains('dog.toJson()'), + )), + }, + ); + }); + + test('trait generates named method', () async { + await testBuilder( + fixtureGeneratorBuilder(BuilderOptions.empty), + { + 'a|lib/dog.dart': ''' +class Dog { + final int age; + Dog({required this.age}); +} +''', + 'a|test/fixtures.dart': ''' +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:a/dog.dart'; + +@FixtureFor(Dog, traits: { + 'old': {#age: FixtureTraitValue.literal(20)}, +}) +void fixtures() {} +''', + }, + readerWriter: await _makeReader(), + outputs: { + 'a|test/fixtures.fixture.dart': decodedMatches(allOf( + contains('FixtureDefinition old()'), + contains('age: 20'), + )), + }, + ); + }); + + test('positional constructor params', () async { + await testBuilder( + fixtureGeneratorBuilder(BuilderOptions.empty), + { + 'a|lib/point.dart': ''' +class Point { + final int x; + final int y; + Point(this.x, this.y); +} +''', + 'a|test/fixtures.dart': ''' +import 'package:data_fixture_dart_annotations/data_fixture_dart_annotations.dart'; +import 'package:a/point.dart'; + +@FixtureFor(Point) +void fixtures() {} +''', + }, + readerWriter: await _makeReader(), + outputs: { + 'a|test/fixtures.fixture.dart': decodedMatches(allOf( + contains('_\$PointFixtureFactory extends FixtureFactory'), + contains('extension PointFixture on Point'), + )), + }, + ); + }); + }); +} diff --git a/packages/data_fixture_dart_generator/test/similarity_test.dart b/packages/data_fixture_dart_generator/test/similarity_test.dart new file mode 100644 index 0000000..6a25baf --- /dev/null +++ b/packages/data_fixture_dart_generator/test/similarity_test.dart @@ -0,0 +1,44 @@ +import 'package:data_fixture_dart_generator/src/similarity.dart'; +import 'package:test/test.dart'; + +void main() { + group('normalize', () { + test('camelCase → lowercase', () => expect(normalize('firstName'), 'firstname')); + test('snake_case → lowercase no underscore', () => expect(normalize('first_name'), 'firstname')); + test('already lowercase', () => expect(normalize('email'), 'email')); + }); + + group('jaroWinkler', () { + test('identical strings score 1.0', () => expect(jaroWinkler('email', 'email'), 1.0)); + test('empty strings score 0.0', () => expect(jaroWinkler('', 'email'), 0.0)); + test('firstname vs firstname above threshold', + () => expect(jaroWinkler('firstname', 'firstname'), greaterThanOrEqualTo(0.85))); + test('fn vs firstname below threshold', + () => expect(jaroWinkler('fn', 'firstname'), lessThan(0.85))); + test('bday vs birthday below threshold', + () => expect(jaroWinkler('bday', 'birthday'), lessThan(0.85))); + test('surname vs lastname below threshold', + () => expect(jaroWinkler('surname', 'lastname'), lessThan(0.85))); + }); + + group('bestDictionaryMatch', () { + test('firstName → personFirstName code', () { + final result = bestDictionaryMatch('firstName'); + expect(result, 'faker.person.firstName()'); + }); + test('email → internetEmail code', () { + final result = bestDictionaryMatch('email'); + expect(result, 'faker.internet.email()'); + }); + test('unknownXyz → null (below threshold)', () { + final result = bestDictionaryMatch('unknownXyz'); + expect(result, isNull); + }); + // 'userEmail' normalizes to 'useremail' which scores ~0.44 against 'email' + // (Jaro-Winkler penalizes the extra prefix heavily), so no match is expected. + test('userEmail → null (useremail too different from email)', () { + final result = bestDictionaryMatch('userEmail'); + expect(result, isNull); + }); + }); +} diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 96d8fd3..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,334 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - url: "https://pub.dartlang.org" - source: hosted - version: "37.0.0" - analyzer: - dependency: transitive - description: - name: analyzer - url: "https://pub.dartlang.org" - source: hosted - version: "3.4.0" - args: - dependency: transitive - description: - name: args - url: "https://pub.dartlang.org" - source: hosted - version: "2.3.0" - async: - dependency: transitive - description: - name: async - url: "https://pub.dartlang.org" - source: hosted - version: "2.5.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.15.0" - convert: - dependency: transitive - description: - name: convert - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1" - coverage: - dependency: transitive - description: - name: coverage - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - crypto: - dependency: transitive - description: - name: crypto - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1" - faker: - dependency: "direct main" - description: - name: faker - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" - file: - dependency: transitive - description: - name: file - url: "https://pub.dartlang.org" - source: hosted - version: "6.1.2" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.2" - glob: - dependency: transitive - description: - name: glob - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.2" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - url: "https://pub.dartlang.org" - source: hosted - version: "3.2.0" - http_parser: - dependency: transitive - description: - name: http_parser - url: "https://pub.dartlang.org" - source: hosted - version: "4.0.0" - io: - dependency: transitive - description: - name: io - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.3" - js: - dependency: transitive - description: - name: js - url: "https://pub.dartlang.org" - source: hosted - version: "0.6.4" - logging: - dependency: transitive - description: - name: logging - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.2" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.11" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.7.0" - mime: - dependency: transitive - description: - name: mime - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" - node_preamble: - dependency: transitive - description: - name: node_preamble - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.1" - package_config: - dependency: transitive - description: - name: package_config - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.2" - path: - dependency: transitive - description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - pool: - dependency: transitive - description: - name: pool - url: "https://pub.dartlang.org" - source: hosted - version: "1.5.0" - pub_semver: - dependency: transitive - description: - name: pub_semver - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.1" - shelf: - dependency: transitive - description: - name: shelf - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0" - shelf_static: - dependency: transitive - description: - name: shelf_static - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - source_maps: - dependency: transitive - description: - name: source_maps - url: "https://pub.dartlang.org" - source: hosted - version: "0.10.10" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - test: - dependency: "direct dev" - description: - name: test - url: "https://pub.dartlang.org" - source: hosted - version: "1.20.2" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.9" - test_core: - dependency: transitive - description: - name: test_core - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.11" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - vm_service: - dependency: transitive - description: - name: vm_service - url: "https://pub.dartlang.org" - source: hosted - version: "8.2.2" - watcher: - dependency: transitive - description: - name: watcher - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" - yaml: - dependency: transitive - description: - name: yaml - url: "https://pub.dartlang.org" - source: hosted - version: "3.1.0" -sdks: - dart: ">=2.16.0-100.0.dev <3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index f2320e3..58e260f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,14 +1,9 @@ -name: data_fixture_dart -description: Creation of data model easily, with no headache. -version: 3.0.0 -repository: https://github.com/MyLittleSuite/data_fixture_dart -issue_tracker: https://github.com/MyLittleSuite/data_fixture_dart/issues +name: data_fixture_dart_workspace +description: Melos workspace root — not published. +publish_to: none environment: - sdk: ">=2.12.0 <3.0.0" - -dependencies: - faker: ^2.0.0 + sdk: ">=3.7.0 <4.0.0" dev_dependencies: - test: ^1.20.2 + melos: ^7.0.0 diff --git a/renovate.json b/renovate.json index 39a2b6e..5db72dd 100644 --- a/renovate.json +++ b/renovate.json @@ -1,6 +1,6 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:base" + "config:recommended" ] }