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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/go_router_builder/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
## NEXT
## 3.1.0

- Restricts `build` to versions less than 2.5.0.
- Updates dependencies to use the latest `analyzer`, `build`, and `source_gen`.
- Updates dev dependencies to use the latest `build_test`.
- Migrates to the `element2` API.
- Improves test code formatting consistency.
- Updates minimum supported SDK version to Flutter 3.29/Dart 3.7.

## 3.0.1

Expand Down
11 changes: 4 additions & 7 deletions packages/go_router_builder/lib/src/go_router_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import 'dart:async';

import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
Expand Down Expand Up @@ -68,23 +68,20 @@ ${getters.map((String e) => "$e,").join('\n')}
annotatedElement.annotation,
);
getters.add(generatedValue.routeGetterName);
for (final String value in generatedValue) {
assert(value.length == value.trim().length);
values.add(value);
}
values.addAll(generatedValue.members);
}
}

InfoIterable _generateForAnnotatedElement(
Element element,
Element2 element,
Copy link
Contributor

Choose a reason for hiding this comment

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

looking at the doc https://pub.dev/documentation/analyzer/latest/dart_element_element/Element2.html

should we keep the original class name

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@chunhtai
Element2 will be deprecated starting with analyzer v8.0.0. Since analyzer: “>=7.4.0 <8.0.0” is set, Element2 is not deprecated.

https://pub.dev/documentation/analyzer/7.7.1/dart_element_element2/Element2-class.html
https://pub.dev/documentation/analyzer/8.0.0/dart_element_element/Element2.html

Other libraries are also replacing Element2 in the same way.

google/json_serializable.dart#1504
rrousselGit/freezed#1276

Copy link
Contributor

@chunhtai chunhtai Jul 30, 2025

Choose a reason for hiding this comment

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

can we pin analyzer 8.0.0 and above directly?

Copy link
Contributor Author

@koji-1009 koji-1009 Jul 30, 2025

Choose a reason for hiding this comment

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

ConstantReader annotation,
) {
final String typedAnnotation =
withoutNullability(annotation.objectValue.type!.getDisplayString());
final String type =
typedAnnotation.substring(0, typedAnnotation.indexOf('<'));
final String routeData = _annotations[type]!;
if (element is! ClassElement) {
if (element is! ClassElement2) {
Copy link
Contributor

Choose a reason for hiding this comment

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

throw InvalidGenerationSourceError(
'The @$type annotation can only be applied to classes.',
element: element,
Expand Down
86 changes: 43 additions & 43 deletions packages/go_router_builder/lib/src/route_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'dart:collection';

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:collection/collection.dart';
Expand Down Expand Up @@ -60,14 +60,14 @@ class ShellRouteConfig extends RouteBaseConfig {

@override
Iterable<String> classDeclarations() {
if (routeDataClass.unnamedConstructor == null) {
if (routeDataClass.unnamedConstructor2 == null) {

Choose a reason for hiding this comment

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

medium

The migration to Element2 API involves changes to how constructor elements are accessed. Consider updating the error message to reflect the new API usage and improve clarity for developers debugging potential issues related to constructor access.

throw InvalidGenerationSourceError(
'The ShellRouteData "$_className" class must have an unnamed constructor.',
element: routeDataClass,
);
}

final bool isConst = routeDataClass.unnamedConstructor!.isConst;
final bool isConst = routeDataClass.unnamedConstructor2!.isConst;
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

and all other places


return <String>[
'''
Expand Down Expand Up @@ -119,7 +119,7 @@ class StatefulShellRouteConfig extends RouteBaseConfig {
Iterable<String> classDeclarations() => <String>[
'''
extension $_extensionName on $_className {
static $_className _fromState(GoRouterState state) =>${routeDataClass.unnamedConstructor!.isConst ? ' const' : ''} $_className();
static $_className _fromState(GoRouterState state) =>${routeDataClass.unnamedConstructor2!.isConst ? ' const' : ''} $_className();
}
'''
];
Expand Down Expand Up @@ -258,8 +258,8 @@ class GoRouteConfig extends RouteBaseConfig {
return "'$location'";
}

ParameterElement? get _extraParam => _ctor.parameters
.singleWhereOrNull((ParameterElement element) => element.isExtraField);
FormalParameterElement? get _extraParam => _ctor.formalParameters
.singleWhereOrNull((FormalParameterElement element) => element.isExtraField);

String get _fromStateConstructor {
final StringBuffer buffer = StringBuffer('=>');
Expand All @@ -271,7 +271,7 @@ class GoRouteConfig extends RouteBaseConfig {
}

buffer.writeln('$_className(');
for (final ParameterElement param in <ParameterElement>[
for (final FormalParameterElement param in <FormalParameterElement>[
..._ctorParams,
..._ctorQueryParams,
if (_extraParam != null) _extraParam!,
Expand All @@ -293,10 +293,10 @@ class GoRouteConfig extends RouteBaseConfig {
return '\n$_className get $selfFieldName => this as $_className;\n';
}

String _decodeFor(ParameterElement element) {
String _decodeFor(FormalParameterElement element) {
if (element.isRequired) {
if (element.type.nullabilitySuffix == NullabilitySuffix.question &&
_pathParams.contains(element.name)) {
_pathParams.contains(element.displayName)) {

Choose a reason for hiding this comment

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

high

The migration to Element2 API involves changes to how parameter names are accessed. Ensure that the display name is correctly used throughout the codebase to maintain consistency and prevent unexpected behavior.

throw InvalidGenerationSourceError(
'Required parameters in the path cannot be nullable.',
element: element,
Expand All @@ -310,7 +310,7 @@ class GoRouteConfig extends RouteBaseConfig {
}

if (element.isNamed) {
return '${element.name}: $fromStateExpression,';
return '${element.displayName}: $fromStateExpression,';
}

throw InvalidGenerationSourceError(
Expand All @@ -320,7 +320,7 @@ class GoRouteConfig extends RouteBaseConfig {
}

String _encodeFor(String fieldName) {
final PropertyAccessorElement? field = _field(fieldName);
final PropertyAccessorElement2? field = _field(fieldName);
if (field == null) {
throw InvalidGenerationSourceError(
'Could not find a field for the path parameter "$fieldName".',
Expand All @@ -338,8 +338,8 @@ class GoRouteConfig extends RouteBaseConfig {

final StringBuffer buffer = StringBuffer('queryParams: {\n');

for (final ParameterElement param in _ctorQueryParams) {
final String parameterName = param.name;
for (final FormalParameterElement param in _ctorQueryParams) {
final String parameterName = param.displayName;

final List<String> conditions = <String>[];
if (param.hasDefaultValue) {
Expand Down Expand Up @@ -367,21 +367,21 @@ class GoRouteConfig extends RouteBaseConfig {
return buffer.toString();
}

late final List<ParameterElement> _ctorParams =
_ctor.parameters.where((ParameterElement element) {
if (_pathParams.contains(element.name)) {
late final List<FormalParameterElement> _ctorParams =
_ctor.formalParameters.where((FormalParameterElement element) {
if (_pathParams.contains(element.displayName)) {
return true;
}
return false;
}).toList();

late final List<ParameterElement> _ctorQueryParams = _ctor.parameters
.where((ParameterElement element) =>
!_pathParams.contains(element.name) && !element.isExtraField)
late final List<FormalParameterElement> _ctorQueryParams = _ctor.formalParameters
.where((FormalParameterElement element) =>
!_pathParams.contains(element.displayName) && !element.isExtraField)
.toList();

ConstructorElement get _ctor {
final ConstructorElement? ctor = routeDataClass.unnamedConstructor;
ConstructorElement2 get _ctor {
final ConstructorElement2? ctor = routeDataClass.unnamedConstructor2;

if (ctor == null) {
throw InvalidGenerationSourceError(
Expand Down Expand Up @@ -443,7 +443,7 @@ mixin $_mixinName on GoRouteData {
Iterable<String> _enumDeclarations() {
final Set<InterfaceType> enumParamTypes = <InterfaceType>{};

for (final ParameterElement ctorParam in <ParameterElement>[
for (final FormalParameterElement ctorParam in <FormalParameterElement>[
..._ctorParams,
..._ctorQueryParams,
]) {
Expand Down Expand Up @@ -489,7 +489,7 @@ abstract class RouteBaseConfig {
/// Creates a new [RouteBaseConfig] represented the annotation data in [reader].
factory RouteBaseConfig.fromAnnotation(
ConstantReader reader,
InterfaceElement element,
InterfaceElement2 element,
) {
final RouteBaseConfig definition =
RouteBaseConfig._fromAnnotation(reader, element, null);
Expand All @@ -507,7 +507,7 @@ abstract class RouteBaseConfig {

factory RouteBaseConfig._fromAnnotation(
ConstantReader reader,
InterfaceElement element,
InterfaceElement2 element,
RouteBaseConfig? parent,
) {
assert(!reader.isNull, 'reader should not be null');
Expand All @@ -523,7 +523,7 @@ abstract class RouteBaseConfig {
}

// TODO(kevmoo): validate that this MUST be a subtype of `GoRouteData`
final InterfaceElement classElement = typeParamType.element;
final InterfaceElement2 classElement = typeParamType.element3;

final RouteBaseConfig value;
switch (typeName) {
Expand Down Expand Up @@ -627,7 +627,7 @@ abstract class RouteBaseConfig {
final List<RouteBaseConfig> _children = <RouteBaseConfig>[];

/// The `RouteData` class this class represents.
final InterfaceElement routeDataClass;
final InterfaceElement2 routeDataClass;

/// The parent of this route config.
final RouteBaseConfig? parent;
Expand All @@ -639,11 +639,11 @@ abstract class RouteBaseConfig {
: 'routes';
}

static String? _generateParameterGetterCode(InterfaceElement classElement,
static String? _generateParameterGetterCode(InterfaceElement2 classElement,
{required String parameterName}) {
final String? fieldDisplayName = classElement.fields
.where((FieldElement element) {
if (!element.isStatic || element.name != parameterName) {
final String? fieldDisplayName = classElement.fields2
.where((FieldElement2 element) {
if (!element.isStatic || element.displayName != parameterName) {
return false;
}
if (parameterName
Expand All @@ -665,21 +665,21 @@ abstract class RouteBaseConfig {
}
return true;
})
.map<String>((FieldElement e) => e.displayName)
.map<String>((FieldElement2 e) => e.displayName)
.firstOrNull;

if (fieldDisplayName != null) {
return '${classElement.name}.$fieldDisplayName';
return '${classElement.displayName}.$fieldDisplayName';
}
final String? methodDisplayName = classElement.methods
.where((MethodElement element) {
return element.isStatic && element.name == parameterName;
final String? methodDisplayName = classElement.methods2
.where((MethodElement2 element) {
return element.isStatic && element.displayName == parameterName;
})
.map<String>((MethodElement e) => e.displayName)
.map<String>((MethodElement2 e) => e.displayName)
.firstOrNull;

if (methodDisplayName != null) {
return '${classElement.name}.$methodDisplayName';
return '${classElement.displayName}.$methodDisplayName';
}
return null;
}
Expand Down Expand Up @@ -727,7 +727,7 @@ abstract class RouteBaseConfig {
RouteBase get $_routeGetterName => ${_invokesRouteConstructor()};
''';

String get _className => routeDataClass.name;
String get _className => routeDataClass.displayName;

String get _mixinName => '_\$$_className';

Expand All @@ -749,8 +749,8 @@ $routeDataClassName.$dataConvertionFunctionName(
''';
}

PropertyAccessorElement? _field(String name) =>
routeDataClass.getGetter(name);
PropertyAccessorElement2? _field(String name) =>
routeDataClass.getGetter2(name);

/// The name of `RouteData` subclass this configuration represents.
@protected
Expand Down Expand Up @@ -780,10 +780,10 @@ String _enumMapConst(InterfaceType type) {

final StringBuffer buffer = StringBuffer('const ${enumMapName(type)} = {');

for (final FieldElement enumField in type.element.fields
.where((FieldElement element) => element.isEnumConstant)) {
for (final FieldElement2 enumField in type.element3.fields2
.where((FieldElement2 element) => element.isEnumConstant)) {
buffer.writeln(
'$enumName.${enumField.name}: ${escapeDartString(enumField.name.kebab)},',
'$enumName.${enumField.displayName}: ${escapeDartString(enumField.displayName.kebab)},',
);
}

Expand Down
Loading