From 7a191a480170fc6927aa99a7d6f1aa66938baa94 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 14:19:22 +0000 Subject: [PATCH 1/4] Use leancode_lint 28 with its analyzer plugin Follows the setup in ciach: leancode_lint is a dev dependency on its latest version and its rules run as an analyzer plugin, with cognitive_complexity turned on. The Dart 3.13 that leancode_lint needs is only required to develop the package; consumers never resolve dev dependencies. - CI analyzes with `dart analyze --fatal-infos`, since `flutter analyze` leaves out plugin diagnostics, and checks formatting. - A new `floor` job runs the tests on Flutter 3.19.6, the lower bound in pubspec.yaml, with leancode_lint dropped. - The test and publish jobs and .fvmrc move to Flutter 3.47.8. - prefer_dot_shorthand stays off: the package supports Dart 3.3 and the rule does not check the language version. - unsafe_variance is ignored: the controllers are invariant in their value type by design, and satisfying it would change the public API. - Fixed the new findings: catch parameter names, unneeded `async`, and comments without a leading space in the example. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KXYs8AHH4YLzzVwNqW3rEx --- .fvmrc | 2 +- .github/workflows/publish.yml | 6 +- .github/workflows/test.yml | 49 +++++- analysis_options.yaml | 18 ++ example/.fvmrc | 2 +- example/analysis_options.yaml | 22 +++ example/lib/main.dart | 10 +- example/lib/screens/step_form.dart | 6 +- example/pubspec.lock | 166 +++---------------- example/pubspec.yaml | 2 +- example/test/screens/step_form_test.dart | 2 +- lib/src/field/advanced_field_controller.dart | 4 +- lib/src/field/async_validation.dart | 8 +- lib/src/field/validation_round.dart | 6 +- pubspec.yaml | 2 +- test/src/field/field_async_failure_test.dart | 7 +- test/src/field/field_dependencies_test.dart | 4 +- test/src/field/field_errors_test.dart | 3 +- test/src/form/form_controller_test.dart | 7 +- 19 files changed, 144 insertions(+), 182 deletions(-) diff --git a/.fvmrc b/.fvmrc index ed6c57b3..e312a131 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,4 +1,4 @@ { - "flutter": "3.41.0", + "flutter": "3.47.8", "flavors": {} } \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 48e10df5..de57e6d8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -21,12 +21,14 @@ jobs: - name: Setup Dart uses: dart-lang/setup-dart@v1 with: - sdk: 3.7 + sdk: '3.13' + # `flutter pub publish` resolves the dev dependencies too, and + # leancode_lint needs Dart 3.13. Keep in sync with .fvmrc. - name: Set up Flutter uses: subosito/flutter-action@v2 with: - flutter-version: 3.29.x + flutter-version: '3.47.8' cache: true - name: Publish and release diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bc088b4f..558ec552 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,23 +8,20 @@ on: jobs: test: - name: Flutter ${{ matrix.channel }}${{ matrix.version }} + name: Test and analyze runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - version: '3.29.x' steps: - name: Checkout uses: actions/checkout@v4 + # leancode_lint, a dev dependency, needs Dart 3.13. Keep in sync with + # .fvmrc. - name: Setup Flutter uses: subosito/flutter-action@v2 with: - flutter-version: ${{ matrix.version }} + flutter-version: '3.47.8' cache: true - name: Flutter version @@ -40,8 +37,13 @@ jobs: - name: Download pub dependencies run: flutter pub get + # Not `flutter analyze`: it leaves out the diagnostics of analyzer + # plugins, which is where leancode_lint's own rules come from. - name: Run analyzer - run: flutter analyze + run: dart analyze --fatal-infos + + - name: Check formatting + run: dart format --output=none --set-exit-if-changed lib test example/lib example/test - name: Run tests run: flutter test @@ -54,6 +56,37 @@ jobs: # We don't want it to fail the CI, it's just to see how would `pub publish` behave. run: dart pub publish --dry-run || true + floor: + name: Test on Flutter 3.19 (lower bound) + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + # The lowest Flutter that pubspec.yaml allows. + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.19.6' + cache: true + + # Consumers never resolve dev dependencies, so the package itself still + # resolves on this SDK; leancode_lint, which needs Dart 3.13, does not. + - name: Drop leancode_lint + run: sed -i '/^ leancode_lint:/d' pubspec.yaml + + # The example app is not part of what consumers get and still depends on + # leancode_lint, so it is left out: `dart pub get --no-example` skips it, + # where `flutter pub get --no-example` and the implicit `pub get` of + # `flutter test` would still touch it. + - name: Download pub dependencies + run: dart pub get --no-example + + - name: Run tests + run: flutter test --no-pub + pana-score: name: pana score diff --git a/analysis_options.yaml b/analysis_options.yaml index a7d79ad9..7d1527a4 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -16,4 +16,22 @@ analyzer: - windows/** - macos/** - linux/** + errors: + # The controllers hold callbacks over their value type (validators, value + # setters, builders), so they are invariant in it by design. Satisfying + # the rule would mean changing the public API. + unsafe_variance: ignore +# leancode_lint's own rules run as an analyzer plugin, so they need the Dart 3.13 +# SDK that leancode_lint requires. That SDK is only needed to develop the package: +# consumers never resolve dev dependencies, and the `floor` job in +# .github/workflows/test.yml checks that the package still works on the lower +# bound in pubspec.yaml. +plugins: + leancode_lint: + version: ^28.0.0 + diagnostics: + cognitive_complexity: true + # Off: dot shorthands need Dart 3.10, and the package supports Dart 3.3. + # The rule does not check the language version. + prefer_dot_shorthand: false diff --git a/example/.fvmrc b/example/.fvmrc index 0c75e237..e312a131 100644 --- a/example/.fvmrc +++ b/example/.fvmrc @@ -1,4 +1,4 @@ { - "flutter": "3.29.0", + "flutter": "3.47.8", "flavors": {} } \ No newline at end of file diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index 4af9cbc4..bebd795e 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -1 +1,23 @@ include: package:leancode_lint/analysis_options.yaml + +analyzer: + exclude: + - build/** + - android/** + - ios/** + - web/** + - windows/** + - macos/** + - linux/** + errors: + # The field widgets are generic over the controller's value type and take + # callbacks over it, the same as the controllers in the package. + unsafe_variance: ignore + +# The same plugin setup as the package's analysis_options.yaml. +plugins: + leancode_lint: + version: ^28.0.0 + diagnostics: + cognitive_complexity: true + prefer_dot_shorthand: false diff --git a/example/lib/main.dart b/example/lib/main.dart index c844375c..23131992 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -26,27 +26,27 @@ class Routes { } enum ValidationError { - //common + // common empty, toShort, toLong, - //password related + // password related noNumber, noSpecialChar, noUpperCase, noLowerCase, doesNotMatch, - //email related + // email related invalidEmail, emailTaken, emailCheckUnavailable, - //quiz related + // quiz related invalidAnswer, - //step form related + // step form related mustAccept, } diff --git a/example/lib/screens/step_form.dart b/example/lib/screens/step_form.dart index 2260f416..fd064fe1 100644 --- a/example/lib/screens/step_form.dart +++ b/example/lib/screens/step_form.dart @@ -189,10 +189,10 @@ class _StepControls extends StatelessWidget { if (form.isLastStep) const _SubmitButton() else - // `next()` awaits the page's async checks, so the button awaits it - // too — never a bare VoidCallback. + // `next()` awaits the page's async checks; the tear-off hands the + // button its Future instead of wrapping it in a bare VoidCallback. ElevatedButton( - onPressed: () async => form.next(), + onPressed: form.next, child: const Text('Next'), ), ], diff --git a/example/pubspec.lock b/example/pubspec.lock index 804346c8..721155f3 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f + sha256: "9bfb8122e2505aca17219018593c384a87166cfe860d11c7ae927e7f978eb05f" url: "https://pub.dev" source: hosted - version: "85.0.0" + version: "110.0.0" advanced_forms: dependency: "direct main" description: @@ -16,22 +16,30 @@ packages: relative: true source: path version: "0.2.4" + analysis_server_plugin: + dependency: transitive + description: + name: analysis_server_plugin + sha256: b94419fc81828edb7870340cb64b8648e9515613d7647309d3d0ea605c6d1f95 + url: "https://pub.dev" + source: hosted + version: "0.3.25" analyzer: dependency: transitive description: name: analyzer - sha256: f4ad0fea5f102201015c9aae9d93bc02f75dd9491529a8c21f88d17a8523d44c + sha256: "74c36179c4de542efea24c9dff78324375e4b118a2c1e14e8a7659f457947f8d" url: "https://pub.dev" source: hosted - version: "7.6.0" + version: "14.6.0" analyzer_plugin: dependency: transitive description: name: analyzer_plugin - sha256: a5ab7590c27b779f3d4de67f31c4109dbe13dd7339f86461a6f2a8ab2594d8ce + sha256: d218f0916ff564cfd9dd032d11e5a990059cb66f88cc741ec6e5e55abdebefcc url: "https://pub.dev" source: hosted - version: "0.13.4" + version: "0.14.19" args: dependency: transitive description: @@ -64,30 +72,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" - url: "https://pub.dev" - source: hosted - version: "2.0.4" - ci: - dependency: transitive - description: - name: ci - sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" - url: "https://pub.dev" - source: hosted - version: "0.1.0" - cli_util: - dependency: transitive - description: - name: cli_util - sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c - url: "https://pub.dev" - source: hosted - version: "0.4.2" clock: dependency: transitive description: @@ -120,46 +104,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.7" - custom_lint: - dependency: transitive - description: - name: custom_lint - sha256: "9656925637516c5cf0f5da018b33df94025af2088fe09c8ae2ca54c53f2d9a84" - url: "https://pub.dev" - source: hosted - version: "0.7.6" - custom_lint_builder: - dependency: transitive - description: - name: custom_lint_builder - sha256: "6cdc8e87e51baaaba9c43e283ed8d28e59a0c4732279df62f66f7b5984655414" - url: "https://pub.dev" - source: hosted - version: "0.7.6" - custom_lint_core: - dependency: transitive - description: - name: custom_lint_core - sha256: "31110af3dde9d29fb10828ca33f1dce24d2798477b167675543ce3d208dee8be" - url: "https://pub.dev" - source: hosted - version: "0.7.5" - custom_lint_visitor: - dependency: transitive - description: - name: custom_lint_visitor - sha256: "4a86a0d8415a91fbb8298d6ef03e9034dc8e323a599ddc4120a0e36c433983a2" - url: "https://pub.dev" - source: hosted - version: "1.0.0+7.7.0" dart_style: dependency: transitive description: name: dart_style - sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb" + sha256: a0e4f65339b31eb6a9e6a12e14b4682a48d46fbc54a82f4a702396fbdbac63a7 url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.14" equatable: dependency: transitive description: @@ -184,14 +136,6 @@ packages: 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" flutter: dependency: "direct main" description: flutter @@ -202,14 +146,6 @@ packages: description: flutter source: sdk version: "0.0.0" - freezed_annotation: - dependency: transitive - description: - name: freezed_annotation - sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" - url: "https://pub.dev" - source: hosted - version: "3.1.0" glob: dependency: transitive description: @@ -218,22 +154,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" - hotreloader: - dependency: transitive - description: - name: hotreloader - sha256: "66871df468fc24eee81f1a0a7cb98acc104716f9b7376d355437b48d633c4ebf" - url: "https://pub.dev" - source: hosted - version: "4.4.0" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" - source: hosted - version: "4.9.0" leak_tracker: dependency: transitive description: @@ -262,18 +182,10 @@ packages: dependency: "direct dev" description: name: leancode_lint - sha256: "4ce7ba4dd48875e5351494d784fe4a1effea3aecf112d9abcfd120b46dc2742c" + sha256: "5563f15c2e298bfce5c7cfd8dea5050261fc1f4bd74349a9aa85c6e2a616cd55" url: "https://pub.dev" source: hosted - version: "15.1.0" - logging: - dependency: transitive - description: - name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" - source: hosted - version: "1.3.0" + version: "28.0.0" matcher: dependency: transitive description: @@ -338,22 +250,6 @@ packages: 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" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" - url: "https://pub.dev" - source: hosted - version: "0.28.0" sky_engine: dependency: transitive description: flutter @@ -383,14 +279,6 @@ packages: 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: @@ -423,14 +311,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" - uuid: - dependency: transitive - description: - name: uuid - sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd" - url: "https://pub.dev" - source: hosted - version: "4.6.0" vector_math: dependency: transitive description: @@ -463,6 +343,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.3" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: "07c9e63ba42519745182b88ca12264a7ba2484d8239958778dfe4d44fe760488" + url: "https://pub.dev" + source: hosted + version: "2.2.4" sdks: - dart: ">=3.11.0-0 <4.0.0" + dart: ">=3.13.0 <4.0.0" flutter: ">=3.19.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index b7451697..f77ab3f0 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - leancode_lint: ^15.0.0 + leancode_lint: ^28.0.0 flutter: uses-material-design: true diff --git a/example/test/screens/step_form_test.dart b/example/test/screens/step_form_test.dart index ffba5b3d..84616b0b 100644 --- a/example/test/screens/step_form_test.dart +++ b/example/test/screens/step_form_test.dart @@ -159,7 +159,7 @@ void main() { expect(controller.currentStep, controller.address); }); - test('changing the country clears the city', () async { + test('changing the country clears the city', () { final controller = buildController(); controller.address.country.select(Country.poland); diff --git a/lib/src/field/advanced_field_controller.dart b/lib/src/field/advanced_field_controller.dart index 746672c4..dc760786 100644 --- a/lib/src/field/advanced_field_controller.dart +++ b/lib/src/field/advanced_field_controller.dart @@ -187,8 +187,8 @@ class AdvancedFieldController // whoever moved the focus. try { await validate(); - } catch (error, stackTrace) { - _report(name, 'validating after focus loss', error, stackTrace); + } catch (err, st) { + _report(name, 'validating after focus loss', err, st); } } diff --git a/lib/src/field/async_validation.dart b/lib/src/field/async_validation.dart index 4c579a1b..fd0b363b 100644 --- a/lib/src/field/async_validation.dart +++ b/lib/src/field/async_validation.dart @@ -101,8 +101,8 @@ class AsyncValidation { E? _mapFailure(AsyncValidationFailure failure, String? field) { try { return failureToError?.call(failure.error, failure.stackTrace); - } catch (error, stack) { - _report(field, 'mapping the async validation failure', error, stack); + } catch (err, st) { + _report(field, 'mapping the async validation failure', err, st); } return null; } @@ -114,8 +114,8 @@ class AsyncValidation { if (onFailure case final handle?) { try { return await handle(failure.error, failure.stackTrace); - } catch (error, stack) { - _report(field, 'running the onFailure handler', error, stack); + } catch (err, st) { + _report(field, 'running the onFailure handler', err, st); } } else { _report( diff --git a/lib/src/field/validation_round.dart b/lib/src/field/validation_round.dart index 8f00d914..7d79acc9 100644 --- a/lib/src/field/validation_round.dart +++ b/lib/src/field/validation_round.dart @@ -198,12 +198,12 @@ class _ValidationRound { timedOut.future, ]); return (verdict: verdict, failure: null); - } catch (error, stackTrace) { + } catch (err, st) { return ( verdict: null, failure: AsyncValidationFailure( - error: error, - stackTrace: stackTrace, + error: err, + stackTrace: st, timedOut: timedOut.isCompleted, ), ); diff --git a/pubspec.yaml b/pubspec.yaml index 1c92f6c6..360a1cfb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,4 +25,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - leancode_lint: ^15.0.0 + leancode_lint: ^28.0.0 diff --git a/test/src/field/field_async_failure_test.dart b/test/src/field/field_async_failure_test.dart index 216d6865..452aac3d 100644 --- a/test/src/field/field_async_failure_test.dart +++ b/test/src/field/field_async_failure_test.dart @@ -259,9 +259,10 @@ void main() { }); test('is itself reported through FlutterError when it throws', () async { - fieldWithHandler((error, stackTrace) async { - throw ArgumentError('handler exploded'); - }).setValue(10); + fieldWithHandler( + (error, stackTrace) => + Future.error(ArgumentError('handler exploded')), + ).setValue(10); await Future.delayed(const Duration(milliseconds: 600)); diff --git a/test/src/field/field_dependencies_test.dart b/test/src/field/field_dependencies_test.dart index 888e7754..8c1eda05 100644 --- a/test/src/field/field_dependencies_test.dart +++ b/test/src/field/field_dependencies_test.dart @@ -141,7 +141,7 @@ void main() { // A prefilled field the user never touched, which must still react when a // field it depends on changes — the pattern the skill documents. group('validate on an untouched dependent', () { - test('sees a dependency changed before the first call finished', () async { + test('sees a dependency changed before the first call finished', () { final form = _EligibilityForm(); addTearDown(form.dispose); @@ -165,7 +165,7 @@ void main() { expect(form.instructor.error, 'not eligible'); }); - test('reacts from a listener on the watched field', () async { + test('reacts from a listener on the watched field', () { final form = _EligibilityForm(validateOnAircraftChange: true); addTearDown(form.dispose); diff --git a/test/src/field/field_errors_test.dart b/test/src/field/field_errors_test.dart index e33a1a39..8f378fab 100644 --- a/test/src/field/field_errors_test.dart +++ b/test/src/field/field_errors_test.dart @@ -83,8 +83,7 @@ void main() { expect(validated, const [initialValue]); }); - test('a flag write re-derives a status that disagrees with the slots', - () async { + test('a flag write re-derives a status that disagrees with the slots', () { // `AdvancedFieldState` can represent `valid` alongside an error, so a // write that touches only a flag still has to settle the status rather // than carry the stale one through. diff --git a/test/src/form/form_controller_test.dart b/test/src/form/form_controller_test.dart index 333f859d..adca16a5 100644 --- a/test/src/form/form_controller_test.dart +++ b/test/src/form/form_controller_test.dart @@ -1144,8 +1144,7 @@ void main() { }); group('removeSubform', () { - test('detaches a previously added subform without disposing it', - () async { + test('detaches a previously added subform without disposing it', () { form.addSubform(subform); final emissions = _record(form); @@ -1160,7 +1159,7 @@ void main() { subformField.dispose(); }); - test('a detached subform is re-attachable', () async { + test('a detached subform is re-attachable', () { form ..addSubform(subform) ..removeSubform(subform) @@ -1174,7 +1173,7 @@ void main() { subformField.dispose(); }); - test('is noop if form was not added', () async { + test('is noop if form was not added', () { final emissions = _record(form); form.removeSubform(subform); expect(emissions, isEmpty); From a26de18548e2f50a5a054f5e9fa2f513c414fb37 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 15:22:36 +0000 Subject: [PATCH 2/4] Move the docs bundle and the landing page to leancode_lint 28 Both now enable leancode_lint's analyzer plugin, like the package. docs_app/flutter: - Flutter 3.47.8 in docs.yml. Its Dart is 3.13, so the separate Dart setup that the landing page needed is gone. - CI analyzes with `dart analyze --fatal-infos`, since `flutter analyze` leaves out plugin diagnostics. - The generator writes package imports in the registry, laid out the way `dart format` writes them, and also ignores `unnecessary_ignore` next to `unused_import` in the prelude, since most snippets use every import. - unsafe_variance is ignored for the Docs* field widgets, as in the package; prefer_dot_shorthand stays off (floor is Dart 3.8). - islandTheme ignores cognitive_complexity: its score is flat light/dark choices, not nesting. landing: - leancode_lint 28 with the analyzer-12 override kept for jaspr_builder; the analysis server builds the plugin in a package of its own. - prefer_dot_shorthand is on (the landing page is on Dart 3.10+), with its 22 findings fixed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KXYs8AHH4YLzzVwNqW3rEx --- .github/workflows/docs.yml | 24 +-- docs_app/flutter/analysis_options.yaml | 13 ++ .../flutter/lib/support/docs_brightness.dart | 2 +- .../flutter/lib/support/island_frame.dart | 2 + docs_app/flutter/pubspec.lock | 168 +++--------------- docs_app/flutter/pubspec.yaml | 2 +- docs_app/scripts/flutter-examples.mjs | 12 +- landing/analysis_options.yaml | 9 + landing/lib/app.dart | 2 +- landing/lib/components/example_frame.dart | 4 +- landing/lib/components/hero.dart | 2 +- landing/lib/components/sections.dart | 30 ++-- landing/lib/examples.dart | 2 +- landing/lib/highlight.dart | 2 +- landing/pubspec.lock | 4 +- landing/pubspec.yaml | 2 +- 16 files changed, 92 insertions(+), 188 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 6735bbb2..d8013417 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -46,12 +46,9 @@ env: # The version the islands are validated against. Multi-view web embedding is # young enough that the docs bundle pins its own version rather than following # the range the package supports. - FLUTTER_VERSION: '3.41.4' - # The landing page is plain Dart, and its build tooling (Ciach's pins of - # build_web_compilers and leancode_lint) needs a newer SDK than the one this - # Flutter bundles. Installed after Flutter, so it is the `dart` on PATH; - # `flutter …` commands keep using their own. - DART_VERSION: '3.13.0' + # Its Dart (3.13) is also the one the landing page builds with, and what + # leancode_lint needs. + FLUTTER_VERSION: '3.47.8' JASPR_CLI_VERSION: '0.23.4' # One deployment at a time per ref. A newer push to a pull request supersedes @@ -78,11 +75,6 @@ jobs: flutter-version: ${{ env.FLUTTER_VERSION }} cache: true - - name: Setup Dart - uses: dart-lang/setup-dart@v1 - with: - sdk: ${{ env.DART_VERSION }} - - name: Install the Jaspr CLI run: dart pub global activate jaspr_cli "$JASPR_CLI_VERSION" @@ -115,8 +107,10 @@ jobs: run: dart format --output=none --set-exit-if-changed lib/generated lib/support working-directory: docs_app/flutter + # Not `flutter analyze`: it leaves out the diagnostics of analyzer + # plugins, which is where leancode_lint's own rules come from. - name: Analyze the example bundle - run: flutter analyze + run: dart analyze --fatal-infos working-directory: docs_app/flutter # Puts every example through the constraints a real island imposes. @@ -189,12 +183,6 @@ jobs: flutter-version: ${{ env.FLUTTER_VERSION }} cache: true - - name: Setup Dart - if: env.VERCEL_TOKEN != '' - uses: dart-lang/setup-dart@v1 - with: - sdk: ${{ env.DART_VERSION }} - - name: Install the Jaspr CLI if: env.VERCEL_TOKEN != '' run: dart pub global activate jaspr_cli "$JASPR_CLI_VERSION" diff --git a/docs_app/flutter/analysis_options.yaml b/docs_app/flutter/analysis_options.yaml index 958b3fed..5f0f25cc 100644 --- a/docs_app/flutter/analysis_options.yaml +++ b/docs_app/flutter/analysis_options.yaml @@ -12,3 +12,16 @@ analyzer: - windows/** - macos/** - linux/** + errors: + # The Docs* field widgets are generic over the controller's value type and + # take callbacks over it, the same as the controllers in the package. + unsafe_variance: ignore + +# The same plugin setup as the package's analysis_options.yaml. +plugins: + leancode_lint: + version: ^28.0.0 + diagnostics: + cognitive_complexity: true + # Off: dot shorthands need Dart 3.10, and this package's floor is 3.8. + prefer_dot_shorthand: false diff --git a/docs_app/flutter/lib/support/docs_brightness.dart b/docs_app/flutter/lib/support/docs_brightness.dart index ea625430..cf42a640 100644 --- a/docs_app/flutter/lib/support/docs_brightness.dart +++ b/docs_app/flutter/lib/support/docs_brightness.dart @@ -19,7 +19,7 @@ class DocsBrightness extends ValueNotifier { DocsBrightness() : super(_read()) { _observer = web.MutationObserver( - (JSArray _, web.MutationObserver __) { + (JSArray _, web.MutationObserver _) { value = _read(); }.toJS, )..observe( diff --git a/docs_app/flutter/lib/support/island_frame.dart b/docs_app/flutter/lib/support/island_frame.dart index c367fb48..b9141d01 100644 --- a/docs_app/flutter/lib/support/island_frame.dart +++ b/docs_app/flutter/lib/support/island_frame.dart @@ -91,6 +91,8 @@ class _IslandFrameState extends State { /// The same tokens the docs page paints with: near-black surfaces, one lime /// accent, rounded filled inputs. In the light variant the accent becomes a /// fill with dark ink on it — a lime *text* would not be legible on paper. +// The score comes from the flat light/dark choices below, not from nesting. +// ignore: leancode_lint/cognitive_complexity ThemeData islandTheme(Brightness brightness) { final dark = brightness == Brightness.dark; diff --git a/docs_app/flutter/pubspec.lock b/docs_app/flutter/pubspec.lock index bbef80d0..66013bc7 100644 --- a/docs_app/flutter/pubspec.lock +++ b/docs_app/flutter/pubspec.lock @@ -5,33 +5,41 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f + sha256: "9bfb8122e2505aca17219018593c384a87166cfe860d11c7ae927e7f978eb05f" url: "https://pub.dev" source: hosted - version: "85.0.0" + version: "110.0.0" advanced_forms: dependency: "direct main" description: path: "../.." relative: true source: path - version: "0.2.0+3" + version: "0.2.4" + analysis_server_plugin: + dependency: transitive + description: + name: analysis_server_plugin + sha256: b94419fc81828edb7870340cb64b8648e9515613d7647309d3d0ea605c6d1f95 + url: "https://pub.dev" + source: hosted + version: "0.3.25" analyzer: dependency: transitive description: name: analyzer - sha256: f4ad0fea5f102201015c9aae9d93bc02f75dd9491529a8c21f88d17a8523d44c + sha256: "74c36179c4de542efea24c9dff78324375e4b118a2c1e14e8a7659f457947f8d" url: "https://pub.dev" source: hosted - version: "7.6.0" + version: "14.6.0" analyzer_plugin: dependency: transitive description: name: analyzer_plugin - sha256: a5ab7590c27b779f3d4de67f31c4109dbe13dd7339f86461a6f2a8ab2594d8ce + sha256: d218f0916ff564cfd9dd032d11e5a990059cb66f88cc741ec6e5e55abdebefcc url: "https://pub.dev" source: hosted - version: "0.13.4" + version: "0.14.19" args: dependency: transitive description: @@ -64,30 +72,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" - url: "https://pub.dev" - source: hosted - version: "2.0.4" - ci: - dependency: transitive - description: - name: ci - sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" - url: "https://pub.dev" - source: hosted - version: "0.1.0" - cli_util: - dependency: transitive - description: - name: cli_util - sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c - url: "https://pub.dev" - source: hosted - version: "0.4.2" clock: dependency: transitive description: @@ -120,46 +104,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.7" - custom_lint: - dependency: transitive - description: - name: custom_lint - sha256: "9656925637516c5cf0f5da018b33df94025af2088fe09c8ae2ca54c53f2d9a84" - url: "https://pub.dev" - source: hosted - version: "0.7.6" - custom_lint_builder: - dependency: transitive - description: - name: custom_lint_builder - sha256: "6cdc8e87e51baaaba9c43e283ed8d28e59a0c4732279df62f66f7b5984655414" - url: "https://pub.dev" - source: hosted - version: "0.7.6" - custom_lint_core: - dependency: transitive - description: - name: custom_lint_core - sha256: "31110af3dde9d29fb10828ca33f1dce24d2798477b167675543ce3d208dee8be" - url: "https://pub.dev" - source: hosted - version: "0.7.5" - custom_lint_visitor: - dependency: transitive - description: - name: custom_lint_visitor - sha256: "4a86a0d8415a91fbb8298d6ef03e9034dc8e323a599ddc4120a0e36c433983a2" - url: "https://pub.dev" - source: hosted - version: "1.0.0+7.7.0" dart_style: dependency: transitive description: name: dart_style - sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb" + sha256: a0e4f65339b31eb6a9e6a12e14b4682a48d46fbc54a82f4a702396fbdbac63a7 url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.14" equatable: dependency: transitive description: @@ -184,14 +136,6 @@ packages: 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" flutter: dependency: "direct main" description: flutter @@ -202,14 +146,6 @@ packages: description: flutter source: sdk version: "0.0.0" - freezed_annotation: - dependency: transitive - description: - name: freezed_annotation - sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" - url: "https://pub.dev" - source: hosted - version: "3.1.0" glob: dependency: transitive description: @@ -218,22 +154,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" - hotreloader: - dependency: transitive - description: - name: hotreloader - sha256: "66871df468fc24eee81f1a0a7cb98acc104716f9b7376d355437b48d633c4ebf" - url: "https://pub.dev" - source: hosted - version: "4.4.0" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80" - url: "https://pub.dev" - source: hosted - version: "4.12.0" leak_tracker: dependency: transitive description: @@ -262,18 +182,10 @@ packages: dependency: "direct dev" description: name: leancode_lint - sha256: "4ce7ba4dd48875e5351494d784fe4a1effea3aecf112d9abcfd120b46dc2742c" + sha256: "5563f15c2e298bfce5c7cfd8dea5050261fc1f4bd74349a9aa85c6e2a616cd55" url: "https://pub.dev" source: hosted - version: "15.1.0" - logging: - dependency: transitive - description: - name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" - source: hosted - version: "1.3.0" + version: "28.0.0" matcher: dependency: transitive description: @@ -322,22 +234,6 @@ packages: 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" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" - url: "https://pub.dev" - source: hosted - version: "0.28.0" sky_engine: dependency: transitive description: flutter @@ -367,14 +263,6 @@ packages: 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: @@ -407,14 +295,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" - uuid: - dependency: transitive - description: - name: uuid - sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd" - url: "https://pub.dev" - source: hosted - version: "4.6.0" vector_math: dependency: transitive description: @@ -455,6 +335,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.3" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: "07c9e63ba42519745182b88ca12264a7ba2484d8239958778dfe4d44fe760488" + url: "https://pub.dev" + source: hosted + version: "2.2.4" sdks: - dart: ">=3.11.0-0 <4.0.0" + dart: ">=3.13.0 <4.0.0" flutter: ">=3.32.0" diff --git a/docs_app/flutter/pubspec.yaml b/docs_app/flutter/pubspec.yaml index 1d45e248..d9e92f56 100644 --- a/docs_app/flutter/pubspec.yaml +++ b/docs_app/flutter/pubspec.yaml @@ -24,7 +24,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - leancode_lint: ^15.0.0 + leancode_lint: ^28.0.0 flutter: uses-material-design: true diff --git a/docs_app/scripts/flutter-examples.mjs b/docs_app/scripts/flutter-examples.mjs index c7e9aca8..c6e42aad 100644 --- a/docs_app/scripts/flutter-examples.mjs +++ b/docs_app/scripts/flutter-examples.mjs @@ -116,8 +116,9 @@ function dartLibrary(example) { return ( banner(example.docs.join(", ")) + // The prelude is ours, not the author's: a snippet that happens not to need - // one of these should not have to carry a lint about it. - "// ignore_for_file: unused_import\n" + + // one of these should not have to carry a lint about it. Most snippets do + // use all three, which `unnecessary_ignore` would report otherwise. + "// ignore_for_file: unused_import, unnecessary_ignore\n" + "\n" + "import 'package:advanced_forms/advanced_forms.dart';\n" + "import 'package:advanced_forms_docs_islands/support/support.dart';\n" + @@ -132,14 +133,17 @@ function dartLibrary(example) { } function dartRegistry(examples) { - const imports = examples.map(e => `import 'example_${e.id}.dart' as example_${e.id};`).join("\n") + // Laid out the way `dart format` writes them, which CI checks. + const imports = examples + .map(e => `import 'package:advanced_forms_docs_islands/generated/example_${e.id}.dart'\n as example_${e.id};\n`) + .join("") const entries = examples.map(e => ` '${e.id}': example_${e.id}.buildExample,`).join("\n") return ( banner("the docs") + "\n" + + imports + "import 'package:flutter/widgets.dart';\n" + - (imports ? `\n${imports}\n` : "") + "\n" + "/// Every example in this bundle, keyed by the content hash of its Dart\n" + "/// source. `main.dart` looks up the id the host page passed to `addView`.\n" + diff --git a/landing/analysis_options.yaml b/landing/analysis_options.yaml index af8e0ad0..6b0d5bfb 100644 --- a/landing/analysis_options.yaml +++ b/landing/analysis_options.yaml @@ -4,3 +4,12 @@ analyzer: exclude: - build/** - lib/main.server.options.dart + +# The analysis server builds the plugin in a package of its own, so it gets the +# analyzer it needs despite the analyzer override in pubspec.yaml. +plugins: + leancode_lint: + version: ^28.0.0 + diagnostics: + cognitive_complexity: true + prefer_dot_shorthand: true diff --git a/landing/lib/app.dart b/landing/lib/app.dart index 9b42d760..3dcb5a75 100644 --- a/landing/lib/app.dart +++ b/landing/lib/app.dart @@ -19,7 +19,7 @@ class App extends StatelessComponent { .text('Skip to content'), ]), const NavBar(), - Component.element( + .element( tag: 'main', id: 'main', classes: 'af-landing', diff --git a/landing/lib/components/example_frame.dart b/landing/lib/components/example_frame.dart index 62b1968d..462ea7d1 100644 --- a/landing/lib/components/example_frame.dart +++ b/landing/lib/components/example_frame.dart @@ -57,9 +57,9 @@ class ExampleFrame extends StatelessComponent { }, const [], ), - if (layout == ExampleLayout.split) _SourceTabs(example: example), + if (layout == .split) _SourceTabs(example: example), ]), - if (layout == ExampleLayout.split) + if (layout == .split) const figcaption(classes: 'af-example-caption', [ .text( 'Field widgets prefixed Docs are shorthands the documentation ' diff --git a/landing/lib/components/hero.dart b/landing/lib/components/hero.dart index 1c51812b..95c4132d 100644 --- a/landing/lib/components/hero.dart +++ b/landing/lib/components/hero.dart @@ -96,7 +96,7 @@ class Hero extends StatelessComponent { 'sent anywhere.', ), ]), - ExampleFrame(example: demo, layout: ExampleLayout.stage), + ExampleFrame(example: demo, layout: .stage), ]), ]), ], diff --git a/landing/lib/components/sections.dart b/landing/lib/components/sections.dart index a7df609c..81cf6896 100644 --- a/landing/lib/components/sections.dart +++ b/landing/lib/components/sections.dart @@ -29,7 +29,7 @@ class ModelSection extends StatelessComponent { 'render it. It is running on the right — submit it empty.', ), children: [ - ExampleFrame(example: example, layout: ExampleLayout.split), + ExampleFrame(example: example, layout: .split), ul(classes: 'af-checklist', [ li([ strong(rich('One call to `registerFields`')), @@ -102,7 +102,7 @@ class _Mode { const _modes = [ _Mode( - Icon.hand, + .hand, 'manual', 'default', 'Validates on submit.', @@ -110,7 +110,7 @@ const _modes = [ 'an edit clears the error that described the old value.', ), _Mode( - Icon.keyboard, + .keyboard, 'onUserInteraction', 'live', 'Validates on every keystroke.', @@ -118,7 +118,7 @@ const _modes = [ 'debounce, so typing runs one request, not ten.', ), _Mode( - Icon.squareDashed, + .squareDashed, 'onUnfocus', 'on leave', 'Validates when a field loses focus.', @@ -174,7 +174,7 @@ class ValidationSection extends StatelessComponent { ]), ]), div(classes: 'af-section-demo', [ - ExampleFrame(example: example, layout: ExampleLayout.split), + ExampleFrame(example: example, layout: .split), ]), ], ); @@ -199,7 +199,7 @@ class AsyncSection extends StatelessComponent { '`alice`, then `boom`.', ), children: [ - ExampleFrame(example: example, layout: ExampleLayout.split), + ExampleFrame(example: example, layout: .split), ul(classes: 'af-checklist', [ li([ const strong([ @@ -252,59 +252,59 @@ class _Feature { const _features = [ _Feature( - Icon.braces, + .braces, 'Typed field controllers', 'Text, boolean, single-select and multi-select fields, each with its own ' 'value type and its own error type. Plain strings to start, an enum or ' 'a sealed class when the form grows.', ), _Feature( - Icon.combine, + .combine, 'Validators that compose', '`filled`, `atLeastLength`, `notNull`, `mustBeTrue` and the numeric checks, ' 'combined with `&` and `|` — or any `E? Function(T)` you write yourself.', ), _Feature( - Icon.server, + .server, 'Async validation done right', 'Debounced server checks with a timeout, cancellation of stale rounds, a ' 'cached verdict while the value stands, and a failure model that never ' 'leaves a field stuck on “validating”.', ), _Feature( - Icon.mousePointerClick, + .mousePointerClick, 'Three validation modes', 'Validate on submit, on every keystroke, or when a field loses focus. Set ' 'once on the form; a field or a subform can opt out with its own mode.', ), _Feature( - Icon.gitFork, + .gitFork, 'Cross-field logic', '`subscribeToFields` re-runs a rule when its dependencies change; ' '`addRelation` derives one field’s value from another. Repeat-password ' 'and running totals in two lines each.', ), _Feature( - Icon.layers, + .layers, 'Subforms', 'Attach and detach nested controllers. Their fields join the parent’s ' '`validate`, reset, read-only and error handling — one subform per ' 'wizard step, or one per row of a dynamic list.', ), _Feature( - Icon.listChecks, + .listChecks, 'Form-level state', '`canSubmit`, `wasModified`, `validating` and `validationErrors`, derived ' 'from the live tree on every read and ready to bind to a submit button.', ), _Feature( - Icon.lock, + .lock, 'Read-only fields and server errors', 'Freeze a value with `markReadOnly`, push a 422 response in with ' '`setError`, and let the next edit clear it — no bookkeeping.', ), _Feature( - Icon.gauge, + .gauge, 'Granular rebuilds', 'One builder per field. A keystroke rebuilds that field’s subtree and ' 'nothing else, and a no-op write notifies nobody because state is ' diff --git a/landing/lib/examples.dart b/landing/lib/examples.dart index 42f90a62..6371f1e2 100644 --- a/landing/lib/examples.dart +++ b/landing/lib/examples.dart @@ -83,7 +83,7 @@ LandingExample loadExample(String name) { final preview = attributes['preview'] ?? _inferPreview(files); final id = _idFor(preview, file.path); - return LandingExample( + return .new( id: id, preview: preview, title: attributes['title'] ?? '$preview · live example', diff --git a/landing/lib/highlight.dart b/landing/lib/highlight.dart index 100bd056..4cfc6739 100644 --- a/landing/lib/highlight.dart +++ b/landing/lib/highlight.dart @@ -19,7 +19,7 @@ Future initHighlighting() => sh.Highlighter.initialize(['dart']); /// empty. Its text style is required but never rendered. final _theme = sh.HighlighterTheme.fromConfiguration( '{"settings": []}', - sh.TextStyle(foreground: const sh.Color(0)), + .new(foreground: const .new(0)), ); final _highlighter = sh.Highlighter(language: 'dart', theme: _theme); diff --git a/landing/pubspec.lock b/landing/pubspec.lock index e5838266..56aac02d 100644 --- a/landing/pubspec.lock +++ b/landing/pubspec.lock @@ -293,10 +293,10 @@ packages: dependency: "direct dev" description: name: leancode_lint - sha256: "23148aecd1a7b9cbf1c435dc3423a86875f15f76ff020e13fa901138d428b444" + sha256: "5563f15c2e298bfce5c7cfd8dea5050261fc1f4bd74349a9aa85c6e2a616cd55" url: "https://pub.dev" source: hosted - version: "27.0.0" + version: "28.0.0" logging: dependency: transitive description: diff --git a/landing/pubspec.yaml b/landing/pubspec.yaml index de89d6fd..0317d7bb 100644 --- a/landing/pubspec.yaml +++ b/landing/pubspec.yaml @@ -20,7 +20,7 @@ dev_dependencies: build_web_compilers: '>=4.8.5 <4.8.6' dart_style: '>=3.1.8 <3.1.9' jaspr_builder: ^0.23.4 - leancode_lint: ^27.0.0 + leancode_lint: ^28.0.0 dependency_overrides: # leancode_lint wants analyzer 13+, but only its analysis_options.yaml is From 5c1d6472a568cf01e7ad2e6273006bc2ebe8f62b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 15:28:40 +0000 Subject: [PATCH 3/4] Take jaspr from its next branch for the landing page, like ciach jaspr, jaspr_builder and jaspr_cli come from the commit of jaspr's `next` branch that ciach's website uses (9311973c), whose builder supports analyzer 13 and newer. That lifts the analyzer-12 caps on build_runner, build_web_compilers and dart_style and the analyzer override, and lets jaspr_lints run next to leancode_lint, with the same diagnostics as in ciach. Its two findings are fixed: the source tabs use `input(...)`, and the icon paths pass their children last. The rendered page is the same apart from the order of the attributes on the source-tab radio inputs. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KXYs8AHH4YLzzVwNqW3rEx --- .github/workflows/docs.yml | 7 +- docs_app/README.md | 6 +- docs_app/scripts/landing.mjs | 2 +- landing/README.md | 4 +- landing/analysis_options.yaml | 14 ++- landing/lib/components/example_frame.dart | 17 ++- landing/lib/components/icons.dart | 2 +- landing/pubspec.lock | 140 +++++++++++----------- landing/pubspec.yaml | 26 ++-- 9 files changed, 119 insertions(+), 99 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d8013417..5b431c6a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -49,7 +49,8 @@ env: # Its Dart (3.13) is also the one the landing page builds with, and what # leancode_lint needs. FLUTTER_VERSION: '3.47.8' - JASPR_CLI_VERSION: '0.23.4' + # The commit of the jaspr overrides in landing/pubspec.yaml. + JASPR_REF: '9311973c7261b4a37ee2cb526466aa6934854851' # One deployment at a time per ref. A newer push to a pull request supersedes # the preview in flight; on main every push still deploys, in order. @@ -76,7 +77,7 @@ jobs: cache: true - name: Install the Jaspr CLI - run: dart pub global activate jaspr_cli "$JASPR_CLI_VERSION" + run: dart pub global activate --source git https://github.com/schultek/jaspr --git-path packages/jaspr_cli --git-ref "$JASPR_REF" - name: Setup Node uses: actions/setup-node@v4 @@ -185,7 +186,7 @@ jobs: - name: Install the Jaspr CLI if: env.VERCEL_TOKEN != '' - run: dart pub global activate jaspr_cli "$JASPR_CLI_VERSION" + run: dart pub global activate --source git https://github.com/schultek/jaspr --git-path packages/jaspr_cli --git-ref "$JASPR_REF" - name: Setup Node if: env.VERCEL_TOKEN != '' diff --git a/docs_app/README.md b/docs_app/README.md index 28eb6d55..03b34b30 100644 --- a/docs_app/README.md +++ b/docs_app/README.md @@ -19,9 +19,9 @@ this directory: npm run landing:build && npm run dev ``` -Open http://localhost:3000. Node 22, the Flutter SDK and the Dart SDK with the Jaspr CLI -(`dart pub global activate jaspr_cli 0.23.4`) are required. `landing:build` only has to be repeated when `landing/` -changes; `npm run dev` without it serves the docs and 404s on `/`. +Open http://localhost:3000. Node 22, the Flutter SDK and the Jaspr CLI (see `../landing/README.md` for the command) are +required. `landing:build` only has to be repeated when `landing/` changes; `npm run dev` without it serves the docs and +404s on `/`. ## Layout diff --git a/docs_app/scripts/landing.mjs b/docs_app/scripts/landing.mjs index 31bf5843..4e44a3bd 100644 --- a/docs_app/scripts/landing.mjs +++ b/docs_app/scripts/landing.mjs @@ -13,7 +13,7 @@ * * Runs `examples:generate` first: the page needs the example registry it * writes. Needs the Dart SDK (see ../landing/pubspec.yaml for the version) with the - * Jaspr CLI activated: `dart pub global activate jaspr_cli 0.23.4`. It is run + * Jaspr CLI activated (see ../landing/README.md for the command). It is run * through `dart pub global run`, so the `dart` on PATH is the one that builds. * `SITE_URL` overrides the canonical URL baked into the page, for previews. */ diff --git a/landing/README.md b/landing/README.md index 2a061bd3..7c1568ed 100644 --- a/landing/README.md +++ b/landing/README.md @@ -12,7 +12,9 @@ the Fumadocs app in `../docs_app`, which serves this page's build output from it one domain. ```bash -dart pub global activate jaspr_cli 0.23.4 +# The commit of the jaspr overrides in pubspec.yaml, until it is published. +dart pub global activate --source git https://github.com/schultek/jaspr \ + --git-path packages/jaspr_cli --git-ref 9311973c7261b4a37ee2cb526466aa6934854851 dart pub get jaspr serve # http://localhost:8080, rebuilds on change jaspr build # build/jaspr/{index.html,landing.js,landing-icon.svg,fonts/} diff --git a/landing/analysis_options.yaml b/landing/analysis_options.yaml index 6b0d5bfb..62d9aae5 100644 --- a/landing/analysis_options.yaml +++ b/landing/analysis_options.yaml @@ -5,11 +5,21 @@ analyzer: - build/** - lib/main.server.options.dart -# The analysis server builds the plugin in a package of its own, so it gets the -# analyzer it needs despite the analyzer override in pubspec.yaml. plugins: leancode_lint: version: ^28.0.0 diagnostics: cognitive_complexity: true prefer_dot_shorthand: true + # The commit of the jaspr overrides in pubspec.yaml. + jaspr_lints: + git: + url: https://github.com/schultek/jaspr + path: packages/jaspr_lints + ref: 9311973c7261b4a37ee2cb526466aa6934854851 + diagnostics: + prefer_html_components: true + prefer_styles_getter: true + sort_children_last: true + styles_ordering: true + unsafe_imports: true diff --git a/landing/lib/components/example_frame.dart b/landing/lib/components/example_frame.dart index 462ea7d1..df0e3e49 100644 --- a/landing/lib/components/example_frame.dart +++ b/landing/lib/components/example_frame.dart @@ -97,16 +97,13 @@ class _SourceTabs extends StatelessComponent { }, [ for (final (index, file) in example.files.indexed) ...[ - Component.element( - tag: 'input', - attributes: { - 'type': 'radio', - 'name': group, - 'id': '$group-$index', - 'class': 'af-tab-input', - 'aria-label': file.name, - if (index == 0) 'checked': '', - }, + input( + type: .radio, + name: group, + id: '$group-$index', + classes: 'af-tab-input', + checked: index == 0, + attributes: {'aria-label': file.name}, ), label( classes: 'af-example-tab', diff --git a/landing/lib/components/icons.dart b/landing/lib/components/icons.dart index 6b50c4f8..603e8847 100644 --- a/landing/lib/components/icons.dart +++ b/landing/lib/components/icons.dart @@ -152,7 +152,7 @@ enum Icon { 'focusable': 'false', }, [ - for (final d in paths) path(const [], attributes: {'d': d}), + for (final d in paths) path(attributes: {'d': d}, const []), ], ); } diff --git a/landing/pubspec.lock b/landing/pubspec.lock index 56aac02d..0be41a9d 100644 --- a/landing/pubspec.lock +++ b/landing/pubspec.lock @@ -5,42 +5,42 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: a49d6cf99e8d8e7a8e93668d09ced0bbdb954d0b4fccc2f5f9241c6b87fad95c + sha256: "9bfb8122e2505aca17219018593c384a87166cfe860d11c7ae927e7f978eb05f" url: "https://pub.dev" source: hosted - version: "99.0.0" + version: "110.0.0" analysis_server_plugin: dependency: transitive description: name: analysis_server_plugin - sha256: fc372bb4778b901afe4dea2e7f71ec74e6cbe772999d9961a4d8e382bc4fa49c + sha256: b94419fc81828edb7870340cb64b8648e9515613d7647309d3d0ea605c6d1f95 url: "https://pub.dev" source: hosted - version: "0.3.22" + version: "0.3.25" analyzer: - dependency: "direct overridden" + dependency: transitive description: name: analyzer - sha256: "663efa951fb8a45e06f491223a604c93820598f20e6a99c25617a1576065e8b7" + sha256: "74c36179c4de542efea24c9dff78324375e4b118a2c1e14e8a7659f457947f8d" url: "https://pub.dev" source: hosted - version: "12.1.0" + version: "14.6.0" analyzer_plugin: dependency: transitive description: name: analyzer_plugin - sha256: "521fbb02dada7d99d0fc6d10e4b501a07ae1d39ff2a850afbd0ec2279fb81f5c" + sha256: d218f0916ff564cfd9dd032d11e5a990059cb66f88cc741ec6e5e55abdebefcc url: "https://pub.dev" source: hosted - version: "0.14.16" + version: "0.14.19" archive: dependency: transitive description: name: archive - sha256: ace891da0862b0e4cabbb064ee3fd87b2728b898949fdb366d83fe98342c9f19 + sha256: "0d58d1fa1fddf6588251ccf2819f3fb3b6d0452c1b2813ac7413a05592bfbefe" url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "4.5.2" args: dependency: transitive description: @@ -85,34 +85,34 @@ packages: dependency: transitive description: name: build_daemon - sha256: e1d40ef3f7934986d5da2271b1ba07794921ce263e44d622fb6c406d76589e33 + sha256: "39ab65f7c60c19af64b9ef96e6a5d3ab48355f7dddfd98a9079d55a8cb6628fc" url: "https://pub.dev" source: hosted - version: "4.1.6" + version: "4.1.7" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "5367e521935b102bdf1e735d2aab461e36b2edca6517662d088dd04cc39f8d16" + sha256: "6feadd32d90a68253c1f7b4fcff76adb4b41ad32a53f8bad835dc65f01ac9dc5" url: "https://pub.dev" source: hosted - version: "2.15.1" + version: "2.16.2" build_web_compilers: dependency: "direct dev" description: name: build_web_compilers - sha256: ef4bc35e0e335f8520692a333b872446697adce0618cf23c3084715ace641721 + sha256: "0c41652721f5ae373028e13e140806867b7cb73ee00df18d48cf6b5386cbe090" url: "https://pub.dev" source: hosted - version: "4.8.5" + version: "4.8.12" built_collection: dependency: transitive description: name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + sha256: "44922ecebd8e3d0cef743dadd3b54df4e87e2a4c52a29465a1872aa9e192caa8" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.1.2" built_value: dependency: transitive description: @@ -157,10 +157,10 @@ packages: dependency: transitive description: name: crypto - sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + sha256: e3626ae6270383c9e3a549f34c9e4cd26c09ae6a9adaab2bf4737a00340efdc4 url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.0.8" csslib: dependency: transitive description: @@ -170,13 +170,13 @@ packages: source: hosted version: "1.0.2" dart_style: - dependency: "direct dev" + dependency: transitive description: name: dart_style - sha256: a4c1ccfee44c7e75ed80484071a5c142a385345e658fd8bd7c4b5c97e7198f98 + sha256: a0e4f65339b31eb6a9e6a12e14b4682a48d46fbc54a82f4a702396fbdbac63a7 url: "https://pub.dev" source: hosted - version: "3.1.8" + version: "3.1.14" ffi: dependency: transitive description: @@ -217,14 +217,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" - hotreloader: - dependency: transitive - description: - name: hotreloader - sha256: "66871df468fc24eee81f1a0a7cb98acc104716f9b7376d355437b48d633c4ebf" - url: "https://pub.dev" - source: hosted - version: "4.4.0" html: dependency: transitive description: @@ -245,10 +237,10 @@ packages: dependency: transitive description: name: http_multi_server - sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + sha256: "361668bdd71f0ae2dc6d17b75a593520f3c414af6534f9eed1aa72049dd71d18" url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.2.3" http_parser: dependency: transitive description: @@ -268,19 +260,21 @@ packages: jaspr: dependency: "direct main" description: - name: jaspr - sha256: ce9fa2b852a07d538d39c3cf45487184a24a5d329a5c5ec2de47f251995be6aa - url: "https://pub.dev" - source: hosted - version: "0.23.4" + path: "packages/jaspr" + ref: "9311973c7261b4a37ee2cb526466aa6934854851" + resolved-ref: "9311973c7261b4a37ee2cb526466aa6934854851" + url: "https://github.com/schultek/jaspr" + source: git + version: "0.23.5" jaspr_builder: dependency: "direct dev" description: - name: jaspr_builder - sha256: "470ab4f3659e04c5562d68df1904c08e0fb91678e57c90d9583143ec320d2375" - url: "https://pub.dev" - source: hosted - version: "0.23.4" + path: "packages/jaspr_builder" + ref: "9311973c7261b4a37ee2cb526466aa6934854851" + resolved-ref: "9311973c7261b4a37ee2cb526466aa6934854851" + url: "https://github.com/schultek/jaspr" + source: git + version: "0.23.5" json_annotation: dependency: transitive description: @@ -297,6 +291,14 @@ packages: url: "https://pub.dev" source: hosted version: "28.0.0" + listen: + dependency: transitive + description: + name: listen + sha256: "47501a08016a43fcad79252439d723f50f14f88fa7bfd8a177e0a417e5c9e1f2" + url: "https://pub.dev" + source: hosted + version: "1.0.1" logging: dependency: transitive description: @@ -325,10 +327,10 @@ packages: dependency: transitive description: name: package_config - sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + sha256: ffcf4cf3d6c0b74ac43708d9f56625506e8a68aa935abe9d267a7330f320eb5d url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "3.0.0" path: dependency: transitive description: @@ -349,18 +351,18 @@ packages: dependency: transitive description: name: posix - sha256: bc1bad54ad2b735816e31f8d4600cfde6c7839975085ddfbca48b6c9f7c4044e + sha256: "6312deb534ce3812a1b454143740ffe721a17dc3a33bfc978f3a6d7bc5833edf" url: "https://pub.dev" source: hosted - version: "6.5.2" + version: "6.5.3" protobuf: dependency: transitive description: name: protobuf - sha256: "75ec242d22e950bdcc79ee38dd520ce4ee0bc491d7fadc4ea47694604d22bf06" + sha256: "13b6717366aa7ec3792dd62c19c7d33a785708224365f2db23c5ca01f1c1a0f2" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.1.0" pub_semver: dependency: transitive description: @@ -373,10 +375,10 @@ packages: dependency: transitive description: name: pubspec_parse - sha256: c38b81cbf34450b67e0265d73433569d12e34782e30ed769c9cc99c9d5f2e796 + sha256: "9ac81c01ffd6a9cf9b2713288834fbaaf6aebde05ffdf02d380e68b8e06f27d8" url: "https://pub.dev" source: hosted - version: "1.6.0" + version: "1.7.0" scratch_space: dependency: transitive description: @@ -389,10 +391,10 @@ packages: dependency: transitive description: name: shelf - sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + sha256: e75e9cf311d7fd712048038cc17c25199c38536c20330d53e7db058b543d5a48 url: "https://pub.dev" source: hosted - version: "1.4.2" + version: "1.4.3" shelf_gzip: dependency: transitive description: @@ -413,10 +415,10 @@ packages: dependency: transitive description: name: shelf_static - sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + sha256: "480504af9e5323899c5be26a4f6c2fc4437d50954d39842f0290f4ae733d57b4" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.2.0" shelf_web_socket: dependency: transitive description: @@ -429,10 +431,10 @@ packages: dependency: transitive description: name: source_gen - sha256: "11b6047da8e4eb6c643ccac3d0fda3f9c9e11651695f7da24a85ade8aff15a44" + sha256: "833ea7d15b5be79949d3d6c077da0267a0bd93c16897b36a771395a91e63086e" url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "4.3.1" source_maps: dependency: transitive description: @@ -449,6 +451,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.2" + sse: + dependency: transitive + description: + name: sse + sha256: a9a804dbde8bfd369da3b4aa241d44d63a6486a97388c54ec166073d88c52302 + url: "https://pub.dev" + source: hosted + version: "4.2.0" stack_trace: dependency: transitive description: @@ -509,10 +519,10 @@ packages: dependency: transitive description: name: universal_web - sha256: "618a9d8659e9429875f69d4da0fb9c0c143d3d4cdab28b4545502088aa50ed62" + sha256: c58bee740bb9672a3df36cef9b8ee06e75e738d91bcb4309f1ada62a0763a1c0 url: "https://pub.dev" source: hosted - version: "1.1.1+1" + version: "1.1.1+2" uuid: dependency: transitive description: @@ -521,14 +531,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.6.0" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "5f37239c4851efcef929cea7824e76df7f2f0970aef85d66bbc430afa40e72f0" - url: "https://pub.dev" - source: hosted - version: "15.3.0" watcher: dependency: transitive description: @@ -578,4 +580,4 @@ packages: source: hosted version: "2.2.4" sdks: - dart: ">=3.13.0 <3.14.0-z" + dart: ">=3.13.0 <3.15.0-z" diff --git a/landing/pubspec.yaml b/landing/pubspec.yaml index 0317d7bb..3eb574a1 100644 --- a/landing/pubspec.yaml +++ b/landing/pubspec.yaml @@ -13,19 +13,27 @@ dependencies: syntax_highlight_lite: ^0.0.1 dev_dependencies: - # jaspr_builder 0.23 compiles only against analyzer 12, so every build-time - # package that depends on analyzer is capped at its last analyzer-12 release - # and pubspec.lock is committed. Lift this with the next jaspr release. - build_runner: '>=2.10.0 <2.15.2' - build_web_compilers: '>=4.8.5 <4.8.6' - dart_style: '>=3.1.8 <3.1.9' + build_runner: ^2.10.0 + build_web_compilers: ^4.8.5 jaspr_builder: ^0.23.4 leancode_lint: ^28.0.0 +# The commit of jaspr's `next` branch that ciach's website uses, whose builder +# supports analyzer 13 and newer, which leancode_lint needs. Keep the commit the +# same as for `jaspr_cli` in .github/workflows/docs.yml and for `jaspr_lints` in +# analysis_options.yaml, and go back to the version constraints above once it +# is published. dependency_overrides: - # leancode_lint wants analyzer 13+, but only its analysis_options.yaml is - # consumed here; the plugin is resolved separately by the analysis server. - analyzer: ^12.1.0 + jaspr: + git: + url: https://github.com/schultek/jaspr + path: packages/jaspr + ref: 9311973c7261b4a37ee2cb526466aa6934854851 + jaspr_builder: + git: + url: https://github.com/schultek/jaspr + path: packages/jaspr_builder + ref: 9311973c7261b4a37ee2cb526466aa6934854851 jaspr: mode: static From bed274fead283828435ba5cc2010e688864aba49 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 15:39:50 +0000 Subject: [PATCH 4/4] Drop the mention of another repository from the jaspr override comment Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KXYs8AHH4YLzzVwNqW3rEx --- landing/pubspec.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/landing/pubspec.yaml b/landing/pubspec.yaml index 3eb574a1..12f8eedc 100644 --- a/landing/pubspec.yaml +++ b/landing/pubspec.yaml @@ -18,11 +18,10 @@ dev_dependencies: jaspr_builder: ^0.23.4 leancode_lint: ^28.0.0 -# The commit of jaspr's `next` branch that ciach's website uses, whose builder -# supports analyzer 13 and newer, which leancode_lint needs. Keep the commit the -# same as for `jaspr_cli` in .github/workflows/docs.yml and for `jaspr_lints` in -# analysis_options.yaml, and go back to the version constraints above once it -# is published. +# A commit of jaspr's `next` branch, whose builder supports analyzer 13 and +# newer, which leancode_lint needs. Keep the commit the same as for `jaspr_cli` +# in .github/workflows/docs.yml and for `jaspr_lints` in analysis_options.yaml, +# and go back to the version constraints above once it is published. dependency_overrides: jaspr: git: