Skip to content

Commit e144c43

Browse files
authored
fix: enable referencing const values for the builder (#52)
1 parent fec2101 commit e144c43

File tree

3 files changed

+73
-31
lines changed

3 files changed

+73
-31
lines changed

lib/builder.dart

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class _SpecBuilder implements Builder {
5858
if (ast == null) continue;
5959

6060
// Visit the AST to find function declarations
61-
final visitor = _FirebaseFunctionsVisitor(resolver);
61+
final visitor = _FirebaseFunctionsVisitor();
6262
ast.accept(visitor);
6363

6464
// Collect discovered functions and parameters
@@ -97,7 +97,7 @@ class _Namespace {
9797

9898
/// AST visitor that discovers Firebase Functions declarations.
9999
class _FirebaseFunctionsVisitor extends RecursiveAstVisitor<void> {
100-
_FirebaseFunctionsVisitor(this.resolver) {
100+
_FirebaseFunctionsVisitor() {
101101
namespaces = <_Namespace>[
102102
_Namespace(
103103
_extractHttpsFunction,
@@ -213,7 +213,7 @@ class _FirebaseFunctionsVisitor extends RecursiveAstVisitor<void> {
213213
),
214214
];
215215
}
216-
final Resolver resolver;
216+
217217
final Map<String, ParamSpec> params = {};
218218
final Map<String, EndpointSpec> endpoints = {};
219219
late final List<_Namespace> namespaces;
@@ -224,6 +224,7 @@ class _FirebaseFunctionsVisitor extends RecursiveAstVisitor<void> {
224224

225225
@override
226226
void visitMethodInvocation(MethodInvocation node) {
227+
super.visitMethodInvocation(node);
227228
final target = node.target;
228229
final methodName = node.methodName.name;
229230
if (target != null) {
@@ -1035,29 +1036,14 @@ class _FirebaseFunctionsVisitor extends RecursiveAstVisitor<void> {
10351036
String? _extractStringField(
10361037
InstanceCreationExpression node,
10371038
String fieldName,
1038-
) => node.argumentList.arguments
1039-
.whereType<NamedExpression>()
1040-
.where((e) => e.name.label.name == fieldName)
1041-
.map((e) => e.expression)
1042-
.whereType<StringLiteral>()
1043-
.map((e) => e.stringValue!)
1044-
.firstOrNull;
1039+
) {
1040+
final arg = node.argumentList.arguments
1041+
.whereType<NamedExpression>()
1042+
.where((e) => e.name.label.name == fieldName)
1043+
.map((e) => e.expression)
1044+
.firstOrNull;
10451045

1046-
/// Extracts a constant value from an expression.
1047-
Object? _extractConstValue(Expression expression) {
1048-
return switch (expression) {
1049-
StringLiteral() => expression.stringValue,
1050-
IntegerLiteral() => expression.value,
1051-
DoubleLiteral() => expression.value,
1052-
BooleanLiteral() => expression.value,
1053-
ListLiteral() =>
1054-
expression.elements
1055-
.whereType<Expression>()
1056-
.map(_extractConstValue)
1057-
.whereType<dynamic>()
1058-
.toList(),
1059-
_ => null,
1060-
};
1046+
return _extractStringLiteral(arg);
10611047
}
10621048
}
10631049

@@ -1078,6 +1064,55 @@ extension on MethodInvocation {
10781064
}
10791065
}
10801066

1081-
/// Extracts a string literal value.
1082-
String? _extractStringLiteral(Expression? expression) =>
1083-
expression is StringLiteral ? expression.stringValue : null;
1067+
/// Extracts a string literal or constant value.
1068+
String? _extractStringLiteral(Expression? expression) {
1069+
if (expression == null) return null;
1070+
final value = _extractConstValue(expression);
1071+
return value is String ? value : null;
1072+
}
1073+
1074+
/// Extracts a constant value from an expression.
1075+
Object? _extractConstValue(Expression expression) {
1076+
final literal = switch (expression) {
1077+
StringLiteral() => expression.stringValue,
1078+
IntegerLiteral() => expression.value,
1079+
DoubleLiteral() => expression.value,
1080+
BooleanLiteral() => expression.value,
1081+
ListLiteral() =>
1082+
expression.elements
1083+
.whereType<Expression>()
1084+
.map(_extractConstValue)
1085+
.whereType<Object>()
1086+
.toList(),
1087+
_ => null,
1088+
};
1089+
1090+
if (literal != null) return literal;
1091+
1092+
// Try to evaluate as constant if it's an identifier or property access
1093+
Element? element;
1094+
if (expression is SimpleIdentifier) {
1095+
element = expression.element;
1096+
} else if (expression is PrefixedIdentifier) {
1097+
element = expression.element;
1098+
} else if (expression is PropertyAccess) {
1099+
element = expression.propertyName.element;
1100+
}
1101+
1102+
if (element is PropertyAccessorElement) {
1103+
element = element.variable;
1104+
}
1105+
1106+
if (element is VariableElement && element.isConst) {
1107+
final constant = element.computeConstantValue();
1108+
if (constant != null) {
1109+
final reader = ConstantReader(constant);
1110+
if (reader.isString) return reader.stringValue;
1111+
if (reader.isInt) return reader.intValue;
1112+
if (reader.isDouble) return reader.doubleValue;
1113+
if (reader.isBool) return reader.boolValue;
1114+
}
1115+
}
1116+
1117+
return null;
1118+
}

test/fixtures/dart_reference/lib/main.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -676,7 +676,7 @@ void main(List<String> args) async {
676676

677677
// Custom invoker list
678678
firebase.https.onRequest(
679-
name: 'httpsCustomInvoker',
679+
name: _OptionsSilly.httpsCustomInvoker,
680680
options: const HttpsOptions(
681681
invoker: Invoker(['user1@example.com', 'user2@example.com']),
682682
),
@@ -685,7 +685,7 @@ void main(List<String> args) async {
685685

686686
// Pub/Sub with options
687687
firebase.pubsub.onMessagePublished(
688-
topic: 'options-topic',
688+
topic: optionsTopic,
689689
options: const PubSubOptions(
690690
memory: Memory(MemoryOption.mb256),
691691
timeoutSeconds: TimeoutSeconds(120),
@@ -700,6 +700,14 @@ void main(List<String> args) async {
700700
});
701701
}
702702

703+
/// Testing constant expression evaluation
704+
const optionsTopic = 'options-topic';
705+
706+
class _OptionsSilly {
707+
/// Testing constant expression evaluation
708+
static const httpsCustomInvoker = 'httpsCustomInvoker';
709+
}
710+
703711
// =============================================================================
704712
// Data classes for typed callable functions
705713
// =============================================================================

test/snapshots/manifest_snapshot_test.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
///
33
/// This test ensures that the Dart builder generates manifests compatible
44
/// with the Node.js Firebase Functions SDK.
5-
@Tags(['snapshot'])
65
library;
76

87
import 'dart:convert';

0 commit comments

Comments
 (0)